No OneTemporary

File Metadata

Created
Sun, Feb 15, 5:14 PM
diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp
index 483b048..4f92561 100644
--- a/cpp/src/ast.cpp
+++ b/cpp/src/ast.cpp
@@ -1,966 +1,972 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: pgess <v.melnychenko@xreate.org>
* File: ast.cpp
*/
#include "ast.h"
#include "analysis/typeinference.h"
#include "analysis/predefinedanns.h"
#ifdef XREATE_ENABLE_EXTERN
#include "ExternLayer.h"
#endif
#include <stdexcept>
#include <iostream>
//TODO BDecl. forbid multiple body declaration (ExprTyped)
namespace std {
std::size_t
hash<xreate::ScopedSymbol>::operator()(xreate::ScopedSymbol const& s) const {
return s.id ^ (s.version << 2);
}
bool
equal_to<xreate::ScopedSymbol>::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const {
return __x.id == __y.id && __x.version == __y.version;
}
size_t
hash<xreate::Symbol>::operator()(xreate::Symbol const& s) const {
return hash<xreate::ScopedSymbol>()(s.identifier) ^ ((long int) s.scope << 1);
}
bool
equal_to<xreate::Symbol>::operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const {
return __x == __y;
};
}
using namespace std;
namespace xreate {
Atom<Identifier_t>::Atom(const std::wstring& value) {
__value = wstring_to_utf8(value);
}
Atom<Identifier_t>::Atom(std::string && name) : __value(name) {
}
const std::string&
Atom<Identifier_t>::get() const {
return __value;
}
Atom<Number_t>::Atom(wchar_t* value) {
//DEBT reconsider number literal recognition
__value = wcstol(value, 0, 10);
}
Atom<Number_t>::Atom(int value)
: __value(value) {
}
double
Atom<Number_t>::get()const {
return __value;
}
Atom<String_t>::Atom(const std::wstring& value) {
assert(value.size() >= 2);
__value = wstring_to_utf8(value.substr(1, value.size() - 2));
}
Atom<String_t>::Atom(std::string && name) : __value(name) {}
const std::string&
Atom<String_t>::get() const {
return __value;
}
/** \brief xreate::Expression static information*/
class ExpressionHints {
public:
static bool
isStringValueValid(const Expression& e) {
switch (e.__state) {
case Expression::INVALID:
assert(false);
case Expression::IDENT:
case Expression::STRING:
return true;
case Expression::NUMBER:
case Expression::BINDING:
return false;
case Expression::COMPOUND:
{
switch (e.op) {
case Operator::CALL:
return true;
default: return false;
}
}
}
return false;
}
static bool
isDoubleValueValid(const Expression& e) {
switch (e.__state) {
case Expression::NUMBER:
return true;
case Expression::INVALID:
assert(false);
case Expression::IDENT:
case Expression::STRING:
case Expression::BINDING:
return false;
case Expression::COMPOUND: {
switch (e.op) {
case Operator::VARIANT:
return true;
default: return false;
}
}
}
return false;
}
};
class TypeResolver {
public:
TypeResolver(const AST* ast,
TypeResolver * parent,
std::set<std::string> trace,
const std::map<std::string, TypeAnnotation>& scope = std::map<std::string, TypeAnnotation>())
: __ast(ast), __scope(scope), __trace(trace), __parent(parent) {}
ExpandedType
operator()(const TypeAnnotation &t, const std::vector<TypeAnnotation> &args = std::vector<TypeAnnotation>()) {
assert(args.size() == t.bindings.size()); // invalid number of arguments
for (size_t i = 0; i < args.size(); ++i) {
__scope[t.bindings.at(i)] = args.at(i);
}
switch (t.__operator) {
case TypeOperator::ARRAY:{
assert(t.__operands.size() == 1);
Expanded<TypeAnnotation> elTy = this->operator()(t.__operands.at(0));
return ExpandedType(TypeAnnotation(TypeOperator::ARRAY, {elTy.get()}));
}
case TypeOperator::RECORD:
{
std::vector<TypeAnnotation>&& packOperands = expandOperands(t.__operands);
auto typNew = TypeAnnotation(TypeOperator::RECORD, move(packOperands));
typNew.fields = t.fields;
return ExpandedType(move(typNew));
};
case TypeOperator::VARIANT:
{
std::vector<TypeAnnotation>&& packOperands = expandOperands(t.__operands);
auto typNew = TypeAnnotation(TypeOperator::VARIANT, move(packOperands));
typNew.fields = t.fields;
return ExpandedType(move(typNew));
};
case TypeOperator::ALIAS: {
std::string alias = t.__valueCustom;
if (__trace.count(alias)){
assert(false && "Recursive Type");
return ExpandedType(TypeAnnotation());
}
const TypeAnnotation& tyAlias = findType(alias);
std::vector<TypeAnnotation>&& operands = expandOperands(t.__operands);
auto traceNew =__trace;
traceNew.insert(alias);
return TypeResolver(__ast, this, traceNew, __scope)(tyAlias, operands);
};
case TypeOperator::ACCESS:
{
std::string alias = t.__valueCustom;
const TypeAnnotation& ty = findType(alias);
TypeAnnotation tyAggr = this->operator()(ty).get();
for (const string& field : t.fields) {
auto fieldIt = std::find(tyAggr.fields.begin(), tyAggr.fields.end(), field);
assert(fieldIt != tyAggr.fields.end() && "unknown field");
int fieldId = fieldIt - tyAggr.fields.begin();
tyAggr = tyAggr.__operands.at(fieldId);
}
return ExpandedType(tyAggr);
}
case TypeOperator::NONE:
case TypeOperator::VOID:
case TypeOperator::SLAVE:
case TypeOperator::REF: {
return ExpandedType(t);
}
default:
assert(false);
}
assert(false);
return ExpandedType(TypeAnnotation());
}
private:
const AST* __ast;
std::map<std::string, TypeAnnotation> __scope;
std::set<std::string> __trace;
TypeResolver* __parent;
std::vector<TypeAnnotation>
expandOperands(const std::vector<TypeAnnotation>& operands) {
std::vector<TypeAnnotation> pack;
pack.reserve(operands.size());
std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()),
[this](const TypeAnnotation & t) {
return this->operator()(t).get();
});
return pack;
}
TypeAnnotation findType(const std::string& alias){
if (__scope.count(alias)) {
return __scope.at(alias);
} else if (__parent){
return __parent->findType(alias);
} else if (__ast->__registryTypes.count(alias)){
return __ast->__registryTypes.at(alias);
}
assert(false && "Undefined or external type");
return TypeAnnotation();
}
};
TypeAnnotation::TypeAnnotation()
: __operator(TypeOperator::NONE), __value(TypePrimitive::Invalid) {
}
TypeAnnotation::TypeAnnotation(TypePrimitive typ)
: __value(typ)
{}
TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list<TypeAnnotation> operands)
: __operator(op), __operands(operands) {
}
TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector<TypeAnnotation>&& operands)
: __operator(op), __operands(operands) {
}
bool
TypeAnnotation::isValid() const {
return !(__value == TypePrimitive::Invalid && __operator == TypeOperator::NONE);
}
bool
TypeAnnotation::operator<(const TypeAnnotation& t) const {
if (__operator != t.__operator) return __operator < t.__operator;
if (__operator == TypeOperator::NONE)
return __value < t.__value;
if (__operator == TypeOperator::ALIAS || __operator == TypeOperator::ACCESS) {
if (__valueCustom != t.__valueCustom)
return __valueCustom < t.__valueCustom;
}
return __operands < t.__operands;
}
TypeAnnotation
TypeAnnotation::alias(const std::string& alias) {
TypeAnnotation aliasT(TypeOperator::ALIAS, {});
aliasT.__valueCustom = alias;
return aliasT;
}
void
TypeAnnotation::addBindings(std::vector<Atom<Identifier_t>>&& params) {
bindings.reserve(bindings.size() + params.size());
std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()),
[](const Atom<Identifier_t>& ident) {
return ident.get(); });
}
void
TypeAnnotation::addFields(std::vector<Atom<Identifier_t>>&& listFields) {
fields.reserve(fields.size() + listFields.size());
std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()),
[](const Atom<Identifier_t>& ident) {
return ident.get(); });
}
unsigned int Expression::nextVacantId = 0;
Expression::Expression(const Atom<Number_t>& number)
: Expression() {
__state = NUMBER;
op = Operator::INVALID;
__valueD = number.get();
}
Expression::Expression(const Atom<String_t>& a)
: Expression() {
__state = STRING;
op = Operator::INVALID;
__valueS = a.get();
}
Expression::Expression(const Atom<Identifier_t> &ident)
: Expression() {
__state = IDENT;
op = Operator::INVALID;
__valueS = ident.get();
}
Expression::Expression(const Operator &oprt, std::initializer_list<Expression> params)
: Expression() {
__state = COMPOUND;
op = oprt;
if (op == Operator::CALL) {
assert(params.size() > 0);
Expression arg = *params.begin();
assert(arg.__state == Expression::IDENT);
__valueS = std::move(arg.__valueS);
operands.insert(operands.end(), params.begin() + 1, params.end());
return;
}
operands.insert(operands.end(), params.begin(), params.end());
}
void
Expression::setOp(Operator oprt) {
op = oprt;
switch (op) {
case Operator::INVALID:
__state = INVALID;
break;
default:
__state = COMPOUND;
break;
}
}
void
Expression::addArg(Expression &&arg) {
operands.push_back(arg);
}
void
Expression::addTags(const std::list<Expression> tags) const {
std::transform(tags.begin(), tags.end(), std::inserter(this->tags, this->tags.end()),
[](const Expression & tag) {
return make_pair(tag.getValueString(), tag);
});
}
void
Expression::addBindings(std::initializer_list<Atom<Identifier_t>> params) {
addBindings(params.begin(), params.end());
}
void
Expression::bindType(TypeAnnotation t) {
type = move(t);
}
void
Expression::addBlock(ManagedScpPtr scope) {
blocks.push_back(scope.operator->());
}
const std::vector<Expression>&
Expression::getOperands() const {
return operands;
}
double
Expression::getValueDouble() const {
return __valueD;
}
const std::string&
Expression::getValueString() const {
return __valueS;
}
void
Expression::setValue(const Atom<Identifier_t>&& v) {
__valueS = v.get();
}
void Expression::setValueDouble(double value) {
__valueD = value;
}
bool
Expression::isValid() const {
return (__state != INVALID);
}
bool
Expression::isDefined() const {
return (__state != BINDING && __state != INVALID);
}
Expression::Expression()
: __state(INVALID), op(Operator::INVALID), id(nextVacantId++) {
}
namespace details { namespace inconsistent {
std::map<std::string, IntrinsicFn>
AST::__registryIntrinsics = {};
AST::AST() {
Attachments::init<versions::VariableVersion>();
Attachments::init<IdentifierSymbol>();
Attachments::init<ExprAlias_A>();
Attachments::init<TypeInferred>();
Attachments::init<ExprId_A>();
initIntrinsics();
analysis::PredefinedAnns man = analysis::PredefinedAnns::instance();
man.registerVariants(__registryVariants);
man.registerAliases(__registryTypes);
}
void
AST::addInterfaceData(const ASTInterface& interface, Expression&& data) {
__interfacesData.emplace(interface, move(data));
}
void
AST::addDFAData(Expression &&data) {
__dfadata.push_back(data);
}
void
AST::addExternData(ExternData &&entry) {
//__externdata.push_back(entry);
}
void
AST::add(Function* f) {
__functions.push_back(f);
__dictFunctions.emplace(f->getName(), __functions.size() - 1);
}
void
AST::add(MetaRuleAbstract *r) {
__rules.push_back(r);
}
void
AST::add(TypeAnnotation t, Atom<Identifier_t> alias) {
if (t.__operator == TypeOperator::VARIANT) {
for (int i = 0, size = t.fields.size(); i < size; ++i) {
__registryVariants.emplace(t.fields[i], make_pair(t, i));
}
}
__registryTypes.emplace(alias.get(), move(t));
}
ManagedScpPtr
AST::add(CodeScope* scope) {
this->__scopes.push_back(scope);
return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes);
}
std::string
AST::getModuleName() {
const std::string name = "main";
return name;
}
ManagedPtr<Function>
AST::findFunction(const std::string& name) {
int count = __dictFunctions.count(name);
if (!count) {
return ManagedFnPtr::Invalid();
}
assert(count == 1);
auto range = __dictFunctions.equal_range(name);
return ManagedPtr<Function>(range.first->second, &this->__functions);
}
std::list<ManagedFnPtr>
AST::getAllFunctions() const {
const size_t size = __functions.size();
std::list<ManagedFnPtr> result;
for (size_t i = 0; i < size; ++i) {
result.push_back(ManagedFnPtr(i, &this->__functions));
}
return result;
}
//TASK select default specializations
std::list<ManagedFnPtr>
AST::getFnSpecializations(const std::string& fnName) const {
auto functions = __dictFunctions.equal_range(fnName);
std::list<ManagedFnPtr> result;
std::transform(functions.first, functions.second, inserter(result, result.end()),
[this](auto f) {
return ManagedFnPtr(f.second, &this->__functions);
});
return result;
}
template<>
ManagedPtr<Function>
AST::begin<Function>() {
return ManagedPtr<Function>(0, &this->__functions);
}
template<>
ManagedPtr<CodeScope>
AST::begin<CodeScope>() {
return ManagedPtr<CodeScope>(0, &this->__scopes);
}
template<>
ManagedPtr<MetaRuleAbstract>
AST::begin<MetaRuleAbstract>() {
return ManagedPtr<MetaRuleAbstract>(0, &this->__rules);
}
void
AST::recognizeIntrinsic(Expression& fn) const {
assert(fn.op == Operator::CALL_INTRINSIC);
if (!__registryIntrinsics.count(fn.getValueString())){
assert(false);
}
IntrinsicFn fnCode = __registryIntrinsics.at(fn.getValueString());
fn.op = Operator::CALL_INTRINSIC;
fn.setValueDouble((int) fnCode);
}
bool
AST::recognizeVariantConstructor(Expression& function) {
assert(function.op == Operator::CALL);
std::string variant = function.getValueString();
if (!__registryVariants.count(variant)) {
return false;
}
auto record = __registryVariants.at(variant);
const TypeAnnotation& typ = record.first;
function.op = Operator::VARIANT;
function.setValueDouble(record.second);
function.type = typ;
return true;
}
Atom<Number_t>
AST::recognizeVariantConstructor(Atom<Identifier_t> ident) {
std::string variant = ident.get();
assert(__registryVariants.count(variant) && "Can't recognize variant constructor");
auto record = __registryVariants.at(variant);
return Atom<Number_t>(record.second);
}
void
AST::postponeIdentifier(CodeScope* scope, const Expression& id) {
__bucketUnrecognizedIdentifiers.emplace(scope, id);
}
void
AST::recognizePostponedIdentifiers() {
for (const auto& identifier : __bucketUnrecognizedIdentifiers) {
if (!identifier.first->recognizeIdentifier(identifier.second)) {
//exception: Ident not found
std::cout << "Unknown identifier: " << identifier.second.getValueString() << std::endl;
assert(false && "Unknown identifier");
}
}
}
xreate::AST*
AST::finalize() {
//all finalization steps:
recognizePostponedIdentifiers();
return reinterpret_cast<xreate::AST*> (this);
}
void
AST::initIntrinsics(){
if (__registryIntrinsics.size()) return;
__registryIntrinsics = {
{"array_init", IntrinsicFn::ARR_INIT},
{"rec_fields", IntrinsicFn::REC_FIELDS}
};
}
} } //namespace details::incomplete
Expanded<TypeAnnotation>
AST::findType(const std::string& name) {
// find in general scope:
if (__registryTypes.count(name))
return expandType(__registryTypes.at(name));
//if type is unknown keep it as is.
TypeAnnotation t(TypeOperator::ALIAS, {});
t.__valueCustom = name;
return ExpandedType(move(t));
}
Expanded<TypeAnnotation>
AST::expandType(const TypeAnnotation &t) const {
return TypeResolver(this, nullptr, {}, {})(t);
}
ExpandedType
AST::getType(const Expression& e, const TypeAnnotation& expectedT) {
return typeinference::getType(e, expectedT, *this);
}
Function::Function(const Atom<Identifier_t>& name)
: __entry(new CodeScope(0)) {
__name = name.get();
}
void
Function::addTag(Expression&& tag, const TagModifier mod) {
string name = tag.getValueString();
__tags.emplace(move(name), move(tag));
}
const std::map<std::string, Expression>&
Function::getTags() const {
return __tags;
}
CodeScope*
Function::getEntryScope() const {
return __entry;
}
void
Function::addBinding(Atom <Identifier_t>&& name, Expression&& argument, const VNameId hintBindingId) {
__entry->addBinding(move(name), move(argument), hintBindingId);
}
const std::string&
Function::getName() const {
return __name;
}
ScopedSymbol
CodeScope::registerIdentifier(const Expression& identifier, const VNameId hintBindingId) {
versions::VariableVersion version = Attachments::get<versions::VariableVersion>(identifier, versions::VERSION_NONE);
auto result = __identifiers.emplace(identifier.getValueString(), hintBindingId? hintBindingId: __identifiers.size() + 1);
return { result.first->second, version };
}
bool
-CodeScope::recognizeIdentifier(const Expression& identifier) const {
- versions::VariableVersion version = Attachments::get<versions::VariableVersion>(identifier, versions::VERSION_NONE);
- const std::string& name = identifier.getValueString();
+CodeScope::recognizeIdentifier(const Expression& identE) {
+ versions::VariableVersion version = Attachments::get<versions::VariableVersion>(identE, versions::VERSION_NONE);
+ const std::string& identStr = identE.getValueString();
- //search identifier in the current block
- if (__identifiers.count(name)) {
- VNameId id = __identifiers.at(name);
+ //search identifier in the current block
+ if (__identifiers.count(identStr)) {
+ VNameId id = __identifiers.at(identStr);
- Symbol s;
- s.identifier = ScopedSymbol{id, version};
- s.scope = const_cast<CodeScope*> (this);
- Attachments::put<IdentifierSymbol>(identifier, s);
+ Symbol identS;
+ identS.identifier = ScopedSymbol{id, version};
+ identS.scope = const_cast<CodeScope*> (this);
+ Attachments::put<IdentifierSymbol>(identE, identS);
- return true;
- }
+ return true;
+ }
- //search in the parent scope
- if (__parent) {
- return __parent->recognizeIdentifier(identifier);
- }
+ //search in the parent scope
+ bool result = false;
+ if (__parent) {
+ result = __parent->recognizeIdentifier(identE);
+ }
- return false;
+ if (trackExternalSymbs && result){
+ Symbol identS = Attachments::get<IdentifierSymbol>(identE);
+ boundExternalSymbs.insert(identS);
+ }
+
+ return result;
}
ScopedSymbol
-CodeScope::getSymbol(const std::string& alias) {
+CodeScope::findSymbolByAlias(const std::string& alias) {
assert(__identifiers.count(alias));
VNameId id = __identifiers.at(alias);
return {id, versions::VERSION_NONE };
}
void
CodeScope::addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId) {
argument.__state = Expression::BINDING;
__bindings.push_back(var.getValueString());
ScopedSymbol binding = registerIdentifier(var, hintBindingId);
__declarations[binding] = move(argument);
}
Symbol
CodeScope::addDefinition(Expression&& var, Expression&& body) {
ScopedSymbol s = registerIdentifier(var);
__declarations[s] = move(body);
return Symbol{s, this};
}
CodeScope::CodeScope(CodeScope* parent)
: __parent(parent) {
}
CodeScope::~CodeScope() {
}
void
CodeScope::setBody(const Expression &body) {
assert(__declarations.count(ScopedSymbol::RetSymbol)==0 && "Attempt to reassign scope body");
__declarations[ScopedSymbol::RetSymbol] = body;
}
const Expression&
CodeScope::getBody() const{
return __declarations.at(ScopedSymbol::RetSymbol);
}
const Expression&
CodeScope::getDefinition(const Symbol& symbol, bool flagAllowUndefined){
const CodeScope* self = symbol.scope;
return self->getDefinition(symbol.identifier, flagAllowUndefined);
}
const Expression&
CodeScope::getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined) const{
static Expression expressionInvalid;
if (!__declarations.count(symbol)){
if (flagAllowUndefined) return expressionInvalid;
assert(false && "Symbol's declaration not found");
}
return __declarations.at(symbol);
}
void
RuleArguments::add(const Atom<Identifier_t> &arg, DomainAnnotation typ) {
emplace_back(arg.get(), typ);
}
void
RuleGuards::add(Expression&& e) {
push_back(e);
}
MetaRuleAbstract::
MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards)
: __args(std::move(args)), __guards(std::move(guards)) {
}
MetaRuleAbstract::~MetaRuleAbstract() {
}
RuleWarning::
RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom<String_t>&& message)
: MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) {
}
RuleWarning::~RuleWarning() {
}
void
RuleWarning::compile(TranscendLayer& layer) {
//TODO restore addRuleWarning
//layer.addRuleWarning(*this);
}
bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2) {
return (s1.id < s2.id) || (s1.id == s2.id && s1.version < s2.version);
}
bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2) {
return (s1.id == s2.id) && (s1.version == s2.version);
}
bool operator<(const Symbol& s1, const Symbol& s2) {
return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier);
}
bool operator==(const Symbol& s1, const Symbol& s2) {
return (s1.scope == s2.scope) && (s1.identifier == s2.identifier);
}
bool operator< (const ASTSite& s1, const ASTSite& s2){
return s1.id < s2.id;
}
bool operator<(const Expression&a, const Expression&b) {
if (a.__state != b.__state) return a.__state < b.__state;
assert(a.__state != Expression::INVALID);
switch (a.__state) {
case Expression::IDENT:
case Expression::STRING:
return a.getValueString() < b.getValueString();
case Expression::NUMBER:
return a.getValueDouble() < b.getValueDouble();
case Expression::COMPOUND:
{
assert(a.blocks.size() == 0);
assert(b.blocks.size() == 0);
if (a.op != b.op) {
return a.op < b.op;
}
bool flagAValid = ExpressionHints::isStringValueValid(a);
bool flagBValid = ExpressionHints::isStringValueValid(b);
if (flagAValid != flagBValid) {
return flagAValid < flagBValid;
}
if (flagAValid) {
if (a.getValueString() != b.getValueString()) {
return a.getValueString() < b.getValueString();
}
}
flagAValid = ExpressionHints::isDoubleValueValid(a);
flagBValid = ExpressionHints::isDoubleValueValid(b);
if (flagAValid != flagBValid) {
return flagAValid < flagBValid;
}
if (flagAValid) {
if (a.getValueDouble() != b.getValueDouble()) {
return a.getValueDouble() < b.getValueDouble();
}
}
if (a.operands.size() != b.operands.size()) {
return (a.operands.size() < b.operands.size());
}
for (size_t i = 0; i < a.operands.size(); ++i) {
bool result = a.operands[i] < b.operands[i];
if (result) return true;
}
return false;
}
case Expression::BINDING:
case Expression::INVALID:
assert(false);
}
return false;
}
bool
Expression::operator==(const Expression& other) const {
if (this->__state != other.__state) return false;
if (ExpressionHints::isStringValueValid(*this)) {
if (this->__valueS != other.__valueS) return false;
}
if (ExpressionHints::isDoubleValueValid(*this)) {
if (this->__valueD != other.__valueD) return false;
}
if (this->__state != Expression::COMPOUND) {
return true;
}
if (this->op != other.op) {
return false;
}
if (this->operands.size() != other.operands.size()) {
return false;
}
for (size_t i = 0; i<this->operands.size(); ++i) {
if (!(this->operands[i] == other.operands[i])) return false;
}
assert(!this->blocks.size());
assert(!other.blocks.size());
return true;
}
const ScopedSymbol
ScopedSymbol::RetSymbol = ScopedSymbol{0, versions::VERSION_NONE};
Expression
ASTSite::getDefinition() const{
if (Attachments::exists<ExprAlias_A>(id)){
const Symbol& siteS = Attachments::get<ExprAlias_A>(id);
return CodeScope::getDefinition(siteS, true);
}
return Attachments::get<ExprId_A>(id);
}
} //end of namespace xreate
diff --git a/cpp/src/ast.h b/cpp/src/ast.h
index 06517c4..db518d6 100644
--- a/cpp/src/ast.h
+++ b/cpp/src/ast.h
@@ -1,751 +1,754 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: pgess <v.melnychenko@xreate.org>
* File: ast.h
*/
/**
* \file ast.h
* \brief A syntax tree representation and related code
*
* \sa xreate::AST
*/
#ifndef AST_H
#define AST_H
#include "attachments.h"
#include "utils.h"
#include <string>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <climits>
namespace xreate {
struct ScopedSymbol;
struct Symbol;
}
namespace std {
template<>
struct hash<xreate::ScopedSymbol> {
std::size_t operator()(xreate::ScopedSymbol const& s) const;
};
template<>
struct equal_to<xreate::ScopedSymbol> {
bool operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const;
};
template<>
struct hash<xreate::Symbol> {
size_t operator()(xreate::Symbol const& s) const;
};
template<>
struct equal_to<xreate::Symbol> {
bool operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const;
};
}
namespace xreate {
struct String_t {
};
struct Identifier_t {
};
struct Number_t {
};
struct Type_t {
};
template<typename A>
class Atom {
};
//DEBT store line:col for all atoms/identifiers
template<> class
Atom<Identifier_t> {
public:
Atom(const std::wstring& value);
Atom(std::string && name);
const std::string& get() const;
private:
std::string __value;
};
template<>
class Atom<Number_t> {
public:
Atom(wchar_t* value);
Atom(int value);
double get()const;
private:
double __value;
};
template<>
class Atom<String_t> {
public:
Atom(const std::wstring& value);
Atom(std::string && name);
const std::string& get() const;
private:
std::string __value;
};
enum class TypePrimitive {
Invalid, Bool, I8, I32, I64, Int, Float, String
};
enum class TypeOperator {
NONE, VOID, REF, ALIAS, VARIANT, ARRAY, RECORD, ACCESS, SLAVE, GUARD
};
struct struct_tag {
};
const struct_tag tag_struct = struct_tag();
/**
* \brief A type representation to support type system
*
* The class represents type in a denormalized form, i.e. with no arguments and aliases substitution
* \sa AST::expandType()
*/
class TypeAnnotation {
public:
TypeAnnotation();
TypeAnnotation(TypePrimitive typ);
TypeAnnotation(TypeOperator op, std::initializer_list<TypeAnnotation> operands);
TypeAnnotation(TypeOperator op, std::vector<TypeAnnotation>&& operands);
static TypeAnnotation alias(const std::string& alias);
void addBindings(std::vector<Atom<Identifier_t>>&& params);
void addFields(std::vector<Atom<Identifier_t>>&& listFields);
bool operator<(const TypeAnnotation& t) const;
// TypeAnnotation (struct_tag, std::initializer_list<TypePrimitive>);
bool isValid() const;
TypeOperator __operator = TypeOperator::NONE;
std::vector<TypeAnnotation> __operands;
TypePrimitive __value;
std::string __valueCustom;
std::vector<std::string> fields;
std::vector<std::string> bindings;
private:
};
enum class Operator {
INVALID, UNDEF, AND, OR, ADD, SUB, MUL, DIV, MOD,
EQU, NE, NEG, LSS,
LSE, GTR, GTE, LIST,
LIST_INDEX, LIST_RANGE,
CALL, CALL_INTRINSIC, QUERY, QUERY_LATE,
IMPL/* implication */, MAP,
FOLD, FOLD_INF, INDEX,
IF, SWITCH, SWITCH_VARIANT, SWITCH_LATE,
CASE, CASE_DEFAULT, LOGIC_AND,
CONTEXT_RULE, VARIANT, SEQUENCE, UPDATE
};
class Function;
class AST;
class CodeScope;
class MetaRuleAbstract;
typedef ManagedPtr<Function> ManagedFnPtr;
typedef ManagedPtr<CodeScope> ManagedScpPtr;
typedef ManagedPtr<MetaRuleAbstract> ManagedRulePtr;
const ManagedScpPtr NO_SCOPE = ManagedScpPtr(UINT_MAX, 0);
/**
* \brief AST node to represent a single instruction or an annotation
* \attention In case of any changes update \ref xreate::ExpressionHints auxiliary helper as well
*
* %Expression is a generic building block of syntax tree which is able to hold node data
* along with child nodes as operands.
*
* \note For types the %expression-like data structure \ref TypeAnnotation is used rather than Expression itself.
* \sa xreate::AST, xreate::TypeAnnotation
*/
struct Expression {
friend class CodeScope;
friend class TranscendLayer;
friend class CFAPass;
friend class ExpressionHints;
Expression(const Operator &oprt, std::initializer_list<Expression> params);
Expression(const Atom<Identifier_t>& ident);
Expression(const Atom<Number_t>& number);
Expression(const Atom<String_t>& a);
Expression();
void setOp(Operator oprt);
void addArg(Expression&& arg);
void addBindings(std::initializer_list<Atom<Identifier_t>> params);
void bindType(TypeAnnotation t);
template<class InputIt>
void addBindings(InputIt paramsBegin, InputIt paramsEnd);
void addTags(const std::list<Expression> tags) const;
void addBlock(ManagedScpPtr scope);
const std::vector<Expression>& getOperands() const;
double getValueDouble() const;
void setValueDouble(double value);
const std::string& getValueString() const;
void setValue(const Atom<Identifier_t>&& v);
bool isValid() const;
bool isDefined() const;
bool operator==(const Expression& other) const;
/**
* \brief is it string, number, compound operation and so on
*/
enum {
INVALID, COMPOUND, IDENT, NUMBER, STRING, BINDING
} __state = INVALID;
/**
* \brief Valid for compound State. Holds type of compound operator
*/
Operator op;
/**
* \brief Unique id to identify expression within syntax tree
*/
unsigned int id;
/**
* \brief Exact meaning depends on particular instruction
* \details As an example, named lists/structs hold field names in bindings
*/
std::vector<std::string> bindings;
/**
* \brief Holds child instructions as arguments
*/
std::vector<Expression> operands;
/**
* \brief Holds type of instruction's result
*/
TypeAnnotation type;
/**
* \brief Holds additional annotations
*/
mutable std::map<std::string, Expression> tags;
/**
* \brief Child code blocks
* \details For example, If statement holds TRUE-branch as first and FALSE-branch as second block here
*/
std::list<CodeScope*> blocks;
private:
std::string __valueS;
double __valueD;
static unsigned int nextVacantId;
};
bool operator<(const Expression&, const Expression&);
template<class InputIt>
void Expression::addBindings(InputIt paramsBegin, InputIt paramsEnd) {
size_t index = bindings.size();
std::transform(paramsBegin, paramsEnd, std::inserter(bindings, bindings.end()),
[&index, this] (const Atom<Identifier_t> atom) {
std::string key = atom.get();
return key;
});
}
typedef std::list<Expression> ExpressionList;
enum class TagModifier {
NONE, ASSERT, REQUIRE
};
enum class DomainAnnotation {
FUNCTION, VARIABLE
};
class RuleArguments : public std::vector<std::pair<std::string, DomainAnnotation>>
{
public:
void add(const Atom<Identifier_t>& name, DomainAnnotation typ);
};
class RuleGuards : public std::vector<Expression> {
public:
void add(Expression&& e);
};
class TranscendLayer;
class LLVMLayer;
class MetaRuleAbstract {
public:
MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards);
virtual ~MetaRuleAbstract();
virtual void compile(TranscendLayer& layer) = 0;
protected:
RuleArguments __args;
RuleGuards __guards;
};
class RuleWarning : public MetaRuleAbstract {
friend class TranscendLayer;
public:
RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom<String_t>&& message);
virtual void compile(TranscendLayer& layer);
~RuleWarning();
private:
std::string __message;
Expression __condition;
};
typedef unsigned int VNameId;
namespace versions {
typedef int VariableVersion;
const VariableVersion VERSION_NONE = -2;
const VariableVersion VERSION_INIT = 0;
}
template<>
struct AttachmentsDict<versions::VariableVersion> {
typedef versions::VariableVersion Data;
static const unsigned int key = 6;
};
struct ScopedSymbol {
VNameId id;
versions::VariableVersion version;
static const ScopedSymbol RetSymbol;
};
struct Symbol {
ScopedSymbol identifier;
const CodeScope * scope;
};
struct ASTSite {
unsigned int id;
Expression getDefinition() const;
//static Ast registerSite(const Expression& e);
};
struct IdentifierSymbol{};
struct ExprAlias_A{};
struct ExprId_A{};
template<>
struct AttachmentsDict<IdentifierSymbol> {
typedef Symbol Data;
static const unsigned int key = 7;
};
template<>
struct AttachmentsDict<ExprAlias_A> {
typedef Symbol Data;
static const unsigned int key = 9;
};
template<>
struct AttachmentsDict<ExprId_A>{
typedef Expression Data;
static const unsigned int key = 12;
};
typedef std::pair<Expression, TagModifier> Tag;
bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2);
bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2);
bool operator<(const Symbol& s1, const Symbol& s2);
bool operator==(const Symbol& s1, const Symbol& s2);
bool operator< (const ASTSite& s1, const ASTSite& s2);
/**
* \brief AST node to represent a single code block/a scope of visibility
*
* Holds a single expression as a `body` along with set of variable assignments(declarations) used in body's expression.
* \sa xreate::AST
*/
class CodeScope {
friend class Function;
friend class PassManager;
public:
CodeScope(CodeScope* parent = 0);
~CodeScope();
/** \brief Set expression as a body */
void setBody(const Expression& body);
/** \brief Returns current code scope body */
const Expression& getBody() const;
/** \brief Adds variable definition to be used in body as well as in other declarations */
Symbol addDefinition(Expression&& var, Expression&& body);
/** \brief Returns symbols' definition */
static const Expression& getDefinition(const Symbol& symbol, bool flagAllowUndefined = false);
const Expression& getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined = false) const;
/** \brief Adds variable defined elsewhere */
void addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId = 0);
std::vector<std::string> __bindings;
std::map<std::string, VNameId> __identifiers;
CodeScope* __parent;
//TODO move __definitions to SymbolsAttachments data
//NOTE: definition of return type has index 0
std::unordered_map<ScopedSymbol, Expression> __declarations;
std::vector<Expression> tags;
std::vector<Expression> contextRules;
-
+
+ bool trackExternalSymbs = false;
+ std::set<Symbol> boundExternalSymbs;
+
private:
ScopedSymbol registerIdentifier(const Expression& identifier, const VNameId hintBindingId = 0);
public:
- bool recognizeIdentifier(const Expression& identifier) const;
- ScopedSymbol getSymbol(const std::string& alias);
+ bool recognizeIdentifier(const Expression& identE);
+ ScopedSymbol findSymbolByAlias(const std::string& alias);
};
/**
* \brief AST node to represent a single function
*
* Holds an `__entry` entry code scope along with `guard` to denote the different specializations.
* \sa xreate::AST
*/
class Function {
friend class Expression;
friend class CodeScope;
friend class AST;
public:
Function(const Atom<Identifier_t>& name);
/**
* \brief Adds function arguments
*/
void addBinding(Atom <Identifier_t>&& name, Expression&& argument, const VNameId hintBindingId=0);
/**
* \brief Adds additional function annotations
*/
void addTag(Expression&& tag, const TagModifier mod);
const std::string& getName() const;
const std::map<std::string, Expression>& getTags() const;
CodeScope* getEntryScope() const;
CodeScope* __entry;
std::string __name;
Expression guard;
private:
std::map<std::string, Expression> __tags;
};
class ExternData;
typedef Expanded<TypeAnnotation> ExpandedType;
struct TypeInferred{};
template<>
struct AttachmentsDict<TypeInferred> {
typedef ExpandedType Data;
static const unsigned int key = 11;
};
enum ASTInterface {
CFA, DFA, Extern, Adhoc
};
struct FunctionSpecialization {
std::string guard;
size_t id;
};
struct FunctionSpecializationQuery {
std::unordered_set<std::string> context;
};
template<>
struct AttachmentsId<Expression>{
static unsigned int getId(const Expression& expression){
return expression.id;
}
};
template<>
struct AttachmentsId<Symbol>{
static unsigned int getId(const Symbol& s){
return s.scope->__declarations.at(s.identifier).id;
}
};
template<>
struct AttachmentsId<ManagedFnPtr>{
static unsigned int getId(const ManagedFnPtr& f){
const Symbol symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()};
return AttachmentsId<Symbol>::getId(symbolFunction);
}
};
template<>
struct AttachmentsId<CodeScope*>{
static unsigned int getId(const CodeScope* scope){
const Symbol symbolScope{ScopedSymbol::RetSymbol, scope};
return AttachmentsId<Symbol>::getId(symbolScope);
}
};
template<>
struct AttachmentsId<unsigned int>{
static unsigned int getId(const unsigned int id){
return id;
}
};
class TypeResolver;
enum class IntrinsicFn {
ARR_INIT,
REC_FIELDS
};
namespace details { namespace inconsistent {
/**
* \brief AST in an inconsistent form during construction
*
* Represents AST under construction(**inconsistent state**).
* \attention Clients should use rather xreate::AST unless client's code explicitly works with Syntax Tree during construction.
*
* Typically an instance is created by xreate::XreateManager only and filled out by the parser
* \sa xreate::XreateManager::prepare(std::string&&)
*/
class AST {
friend class xreate::TypeResolver;
public:
AST();
/**
* \brief Adds new function to AST
* \param f Function to register
*/
void add(Function* f);
/**
* \brief Adds new declarative rule to AST
* \param r Declarative Rule
*/
void add(MetaRuleAbstract* r);
/** \brief Registers new code block */
ManagedScpPtr add(CodeScope* scope);
/**
* \brief Add new type to AST
* @param t Type definition
* @param alias Typer name
*/
void add(TypeAnnotation t, Atom<Identifier_t> alias);
/** \brief Current module's name */
std::string getModuleName();
/**
* \brief Looks for function with given name
* \param name Function name to find
* \note Requires that only one function exists under given name
* \return Found function
*/
ManagedPtr<Function> findFunction(const std::string& name);
/** \brief Returns all function in AST */
std::list<ManagedFnPtr> getAllFunctions() const;
/**
* \brief Returns all specializations of a function with a given name
* \param fnName function to find
* \return list of found function specializations
*/
std::list<ManagedFnPtr> getFnSpecializations(const std::string& fnName) const;
/**
* \return First element in Functions/Scopes/Rules list depending on template parameter
* \tparam Target either Function or CodeScope or MetaRuleAbstract
*/
template<class Target>
ManagedPtr<Target> begin();
/**
* \brief Performs all necessary steps after AST is built
*
* Performs all finalization steps and moves AST into consistent state represented by xreate::AST
* \sa xreate::AST
* \return AST in consistent state
*/
xreate::AST* finalize();
typedef std::multimap<std::string, unsigned int> FUNCTIONS_REGISTRY;
//std::list<ExternData> __externdata;
std::list<Expression> __dfadata; //TODO move to more appropriate place
std::list<std::string> __rawImports; //TODO move to more appropriate place
std::multimap<ASTInterface, Expression> __interfacesData; //TODO CFA data here.
private:
std::vector<MetaRuleAbstract*> __rules;
std::vector<Function*> __functions;
std::vector<CodeScope*> __scopes;
FUNCTIONS_REGISTRY __dictFunctions;
protected:
std::map<std::string, TypeAnnotation> __registryTypes;
public:
/**
* \brief Stores DFA scheme for later use by DFA Pass
*
* Treats expression as a DFA scheme and feeds to the DFA Pass later
* \param data DFA Scheme
* \sa xreate::DFAPass
*/
void addDFAData(Expression&& data);
/** \brief Stores data for later use by xreate::ExternLayer */
void addExternData(ExternData&& entry);
/**
* \brief Generalized function to store particular data for later use by particular pass
* \param interface Particular Interface
* \param data Particular data
*/
void addInterfaceData(const ASTInterface& interface, Expression&& data);
/**\name Symbols Recognition */
///@{
public:
//TODO revisit enums/variants, move to codescope
/**
* \brief Tries to find out whether expression is Variant constructor
*/
bool recognizeVariantConstructor(Expression& function);
Atom<Number_t> recognizeVariantConstructor(Atom<Identifier_t> ident);
/**
* \brief Postpones unrecognized identifier for future second round of recognition
* \param scope Code block identifier is encountered
* \param id Identifier
*/
void postponeIdentifier(CodeScope* scope, const Expression& id);
/** \brief Second round of identifiers recognition done right after AST is fully constructed */
void recognizePostponedIdentifiers();
void recognizeIntrinsic(Expression& fn) const;
private:
std::map<std::string, std::pair<TypeAnnotation, int>> __registryVariants;
static std::map<std::string, IntrinsicFn> __registryIntrinsics;
std::set<std::pair<CodeScope*, Expression>> __bucketUnrecognizedIdentifiers;
static void initIntrinsics();
public:
///@}
};
template<>
ManagedPtr<Function>
AST::begin<Function>();
template<>
ManagedPtr<CodeScope>
AST::begin<CodeScope>();
template<>
ManagedPtr<MetaRuleAbstract>
AST::begin<MetaRuleAbstract>();
} } // namespace details::incomplete
/**
* \brief AST in a consistent state
*
* AST has two mutually exclusive possible states:
* - an inconsistent state while AST is under construction. Represented by xreate::details::inconsistent::AST
* - a consistent state when AST is built and finalize() is invoked.
*
* This class represents a consistent state and should be used by clients unless client's code explicitly works with AST under construction.
* Consistent AST enables access to additional functions(such as type management).
* \sa xreate::details::inconsistent::AST
*/
class AST : public details::inconsistent::AST {
public:
AST() : details::inconsistent::AST() {}
/**
* \brief Computes fully expanded form of type by substituting all arguments and aliases
* \param t Type to expand
* \return Expdanded or normal form of type
* \sa TypeAnnotation
*/
ExpandedType expandType(const TypeAnnotation &t) const;
/**
* Searches type by given name
* \param name Typename to search
* \return Expanded or normal form of desired type
* \note if type name is not found returns new undefined type with this name
*/
ExpandedType findType(const std::string& name);
/**
* Invokes Type Inference Analysis to find out expanded(normal) form expressions's type
* \sa typeinference.h
* \param e
* \param expectedT expected type
* \return Type of expression
*/
ExpandedType getType(const Expression& e, const TypeAnnotation& expectedT = TypeAnnotation());
};
}
#endif // AST_H
diff --git a/cpp/src/compilation/control.cpp b/cpp/src/compilation/control.cpp
index 6eb5fc6..7780fa9 100644
--- a/cpp/src/compilation/control.cpp
+++ b/cpp/src/compilation/control.cpp
@@ -1,393 +1,393 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: control.cpp
* Author: pgess <v.melnychenko@xreate.org>
*
* Created on June 26, 2016, 6:00 PM
*/
#include "analysis/typeinference.h"
#include "compilation/control.h"
#include "compilation/containers.h"
#include "compilation/transformersaturation.h"
#include "query/containers.h"
#include "llvmlayer.h"
#include "ast.h"
using namespace std;
using namespace llvm;
using namespace xreate;
using namespace xreate::containers;
using namespace xreate::compilation;
#define NAME(x) (hintRetVar.empty()? x : hintRetVar)
#define UNUSED(x) (void)(x)
#define EXPAND_CONTEXT \
LLVMLayer* llvm = context.pass->man->llvm; \
compilation::IBruteScope* scope = context.scope; \
compilation::IBruteFunction* function = context.function;
ControlIR::ControlIR(compilation::Context ctx)
: context(ctx), tyNum(static_cast<llvm::IntegerType*> (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Int))))) {
}
Value*
ControlIR::compileStructIndex(llvm::Value* aggregate, ExpandedType aggrT, const list<string>& indices) {
EXPAND_CONTEXT UNUSED(scope); UNUSED(function);
TypesHelper types(llvm);
llvm::Value* result = aggregate;
assert(indices.size());
for (const string& indexField: indices){
const std::vector<std::string>& tyfields = types.getRecordFields(aggrT);
unsigned idx = -1; bool flagFound = false;
for (unsigned i = 0, size = tyfields.size(); i < size; ++i) {
if (tyfields.at(i) == indexField) {
idx = i; flagFound = true; break;
}
}
if (flagFound){
result = llvm->irBuilder.CreateExtractValue(result, llvm::ArrayRef<unsigned>{idx});
aggrT = typeinference::getSubtype(aggrT, indexField);
} else {
assert(false && "not found required struct field");
}
}
return result;
//dereference pointer
//if (types.isPointerT(t)) {
// llvm::Value* addr = llvm->irBuilder.CreateConstGEP2_32(nullptr, aggregate, 0, i);
// return llvm->irBuilder.CreateLoad(addr);
//}
}
llvm::Value*
ControlIR::compileFold(const Expression& loopE, const std::string& hintAlias) {
EXPAND_CONTEXT
assert(loopE.op == Operator::FOLD);
AST* ast = context.pass->man->root;
//initialization:
const Expression aggrE = loopE.getOperands().at(0);
const ExpandedType& aggrT = ast->getType(aggrE);
llvm::Value* aggrRaw = context.scope->process(aggrE);
IFwdIteratorIR* aggrItIR = IFwdIteratorIR::create(aggrE, aggrT, context);
llvm::Value* idxBeginRaw = aggrItIR->begin(aggrRaw);
llvm::Value* idxEndRaw = aggrItIR->end(aggrRaw);
ExpandedType loopT = ast->getType(loopE);
std::string elAlias = loopE.bindings[0];
std::string accumAlias = loopE.bindings[1];
const Expression& accumE = loopE.getOperands().at(1);
ExpandedType accumT = ast->getType(accumE, loopT.get());
llvm::Type* accumRawT = llvm->toLLVMType(accumT);
llvm::Value* accumInitRaw = scope->process(accumE, accumAlias, accumT.get());
llvm::BasicBlock *blockProlog = llvm::BasicBlock::Create(llvm->llvmContext, "fold_prlg", function->raw);
llvm::BasicBlock *blockHeader = llvm::BasicBlock::Create(llvm->llvmContext, "fold_hdr", function->raw);
llvm::BasicBlock *blockBody = llvm::BasicBlock::Create(llvm->llvmContext, "fold", function->raw);
llvm::BasicBlock *blockFooter = llvm::BasicBlock::Create(llvm->llvmContext, "fold_ftr", function->raw);
llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "fold_eplg", function->raw);
std::unique_ptr<TransformerSaturation> transformerSaturation(new TransformerSaturation(blockProlog, context.pass->managerTransformations));
//Header:
// * create phi
llvm->irBuilder.SetInsertPoint(blockHeader);
llvm::PHINode *accum = llvm->irBuilder.CreatePHI(accumRawT, 2, accumAlias);
accum->addIncoming(accumInitRaw, blockProlog);
llvm::PHINode *idxCurrentRaw = llvm->irBuilder.CreatePHI(idxBeginRaw->getType(), 2, "foldIt");
idxCurrentRaw->addIncoming(idxBeginRaw, blockProlog);
// * loop checks
Value* condRange = llvm->irBuilder.CreateICmpNE(idxCurrentRaw, idxEndRaw);
llvm->irBuilder.CreateCondBr(condRange, blockBody, blockEpilog);
//Body:
llvm->irBuilder.SetInsertPoint(blockBody);
CodeScope* scopeLoop = loopE.blocks.front();
compilation::IBruteScope* loopUnit = function->getBruteScope(scopeLoop);
Value* elIn = aggrItIR->get(aggrRaw, idxCurrentRaw);
loopUnit->bindArg(accum, move(accumAlias));
loopUnit->bindArg(elIn, move(elAlias));
Value* accumNext = loopUnit->compile();
// * Loop saturation checks
bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockFooter, blockEpilog, context);
llvm::BasicBlock* blockSaturation = llvm->irBuilder.GetInsertBlock();
if (!flagSaturationTriggered){
llvm->irBuilder.CreateBr(blockFooter);
}
//Footer:
// * computing next iteration state
llvm->irBuilder.SetInsertPoint(blockFooter);
Value *itLoopNext = aggrItIR->advance(idxCurrentRaw);
accum->addIncoming(accumNext, llvm->irBuilder.GetInsertBlock());
idxCurrentRaw->addIncoming(itLoopNext, llvm->irBuilder.GetInsertBlock());
llvm->irBuilder.CreateBr(blockHeader);
//Prolog:
llvm->irBuilder.SetInsertPoint(context.scope->lastBlockRaw);
llvm->irBuilder.CreateBr(blockProlog);
llvm->irBuilder.SetInsertPoint(blockProlog);
llvm->irBuilder.CreateBr(blockHeader);
// Epilog:
llvm->irBuilder.SetInsertPoint(blockEpilog);
if (!flagSaturationTriggered){
return accum;
}
llvm::PHINode* result = llvm->irBuilder.CreatePHI(accumRawT, 2, hintAlias);
result->addIncoming(accum, blockHeader);
result->addIncoming(accumNext, blockSaturation);
return result;
}
llvm::Value*
ControlIR::compileFoldInf(const Expression& fold, const std::string& hintRetVar) {
EXPAND_CONTEXT
assert(fold.op == Operator::FOLD_INF);
std::string accumName = fold.bindings[0];
llvm::Value* accumInit = scope->process(fold.getOperands()[0]);
llvm::BasicBlock *blockBeforeLoop = llvm->irBuilder.GetInsertBlock();
llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf", function->raw);
llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_next", function->raw);
llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_post", function->raw);
std::unique_ptr<TransformerSaturation> transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations));
llvm->irBuilder.CreateBr(blockLoop);
// * create phi
llvm->irBuilder.SetInsertPoint(blockLoop);
llvm::PHINode *accum = llvm->irBuilder.CreatePHI(accumInit->getType(), 2, accumName);
accum->addIncoming(accumInit, blockBeforeLoop);
// * loop body
CodeScope* scopeLoop = fold.blocks.front();
compilation::IBruteScope* unitLoop = function->getBruteScope(scopeLoop);
unitLoop->bindArg(accum, move(accumName));
Value* accumNext = unitLoop->compile();
// * Loop saturation checks
bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context);
assert(flagSaturationTriggered);
// * computing next iteration state
llvm->irBuilder.SetInsertPoint(blockNext);
accum->addIncoming(accumNext, llvm->irBuilder.GetInsertBlock());
llvm->irBuilder.CreateBr(blockLoop);
// finalization:
llvm->irBuilder.SetInsertPoint(blockAfterLoop);
return accumNext;
}
llvm::Value*
ControlIR::compileIf(const Expression& exprIf, const std::string& hintRetVar) {
EXPAND_CONTEXT
const Expression& condExpr = exprIf.getOperands()[0];
llvm::IRBuilder<>& builder = llvm->irBuilder;
assert(builder.GetInsertBlock() == scope->lastBlockRaw);
//initialization:
llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "ifAfter", function->raw);
llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm->llvmContext, "ifTrue", function->raw);
llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm->llvmContext, "ifFalse", function->raw);
llvm::Value* cond = scope->process(condExpr);
builder.SetInsertPoint(blockTrue);
CodeScope* scopeTrue = exprIf.blocks.front();
llvm::Value* resultTrue = function->getBruteScope(scopeTrue)->compile();
llvm::BasicBlock * blockTrueEnd = builder.GetInsertBlock();
builder.CreateBr(blockEpilog);
builder.SetInsertPoint(blockFalse);
CodeScope* scopeFalse = exprIf.blocks.back();
llvm::Value* resultFalse = function->getBruteScope(scopeFalse)->compile();
llvm::BasicBlock * blockFalseEnd = builder.GetInsertBlock();
builder.CreateBr(blockEpilog);
builder.SetInsertPoint(scope->lastBlockRaw);
llvm->irBuilder.CreateCondBr(cond, blockTrue, blockFalse);
builder.SetInsertPoint(blockEpilog);
- llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if"));
+ llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, hintRetVar);
ret->addIncoming(resultTrue, blockTrueEnd);
ret->addIncoming(resultFalse, blockFalseEnd);
return ret;
}
//TODO Switch: default variant no needed when all possible conditions are considered
llvm::Value*
ControlIR::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) {
EXPAND_CONTEXT UNUSED(function);
AST* root = context.pass->man->root;
llvm::IRBuilder<>& builder = llvm->irBuilder;
assert(exprSwitch.operands.size() >= 2);
assert(exprSwitch.operands[1].op == Operator::CASE_DEFAULT && "No default case in Switch Statement");
int countCases = exprSwitch.operands.size() - 1;
llvm::BasicBlock* blockProlog = builder.GetInsertBlock();
llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw);
builder.SetInsertPoint(blockEpilog);
llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch));
- llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch"));
+ llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, hintRetVar);
llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext);
builder.SetInsertPoint(blockProlog);
llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]);
llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm->llvmContext, "caseDefault", function->raw);
llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(
typeinference::doAutomaticTypeConversion(conditionSwitch, typI8, builder),
blockDefault,
countCases);
for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) {
llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(i), function->raw);
llvm::Value* condCase = function->getBruteScope(exprSwitch.operands[i].blocks.front())->compile();
builder.SetInsertPoint(blockCase);
llvm::Value* resultCase = function->getBruteScope(exprSwitch.operands[i].blocks.back())->compile();
builder.CreateBr(blockEpilog);
ret->addIncoming(resultCase, builder.GetInsertBlock());
builder.SetInsertPoint(blockProlog);
instructionSwitch->addCase(
dyn_cast<llvm::ConstantInt>(
typeinference::doAutomaticTypeConversion(condCase, typI8, builder)),
blockCase);
}
//compile default block:
builder.SetInsertPoint(blockDefault);
CodeScope* scopeDefault = exprSwitch.operands[1].blocks.front();
llvm::Value* resultDefault = function->getBruteScope(scopeDefault)->compile();
builder.CreateBr(blockEpilog);
ret->addIncoming(resultDefault, builder.GetInsertBlock());
builder.SetInsertPoint(blockEpilog);
return ret;
}
llvm::Value*
ControlIR::compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar) {
EXPAND_CONTEXT UNUSED(function);
AST* root = context.pass->man->root;
llvm::IRBuilder<>& builder = llvm->irBuilder;
llvm::Type* typI8= llvm::Type::getInt8Ty(llvm->llvmContext);
const ExpandedType& typVariant = root->getType(exprSwitch.operands.at(0));
llvm::Type* typVariantRaw = llvm->toLLVMType(typVariant);
assert(typVariant->__operands.size() == exprSwitch.operands.size() - 1 && "Ill-formed Switch Variant");
int casesCount = exprSwitch.operands.size();
llvm::BasicBlock* blockProlog = builder.GetInsertBlock();
llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw);
builder.SetInsertPoint(blockEpilog);
llvm::Type* resultType = llvm->toLLVMType(root->getType(exprSwitch));
- llvm::PHINode *ret = builder.CreatePHI(resultType, casesCount, NAME("switch"));
+ llvm::PHINode *ret = builder.CreatePHI(resultType, casesCount, hintRetVar);
builder.SetInsertPoint(blockProlog);
llvm::Value * conditionSwitchRaw = scope->process(exprSwitch.operands.at(0));
llvm::Value* idRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef<unsigned>({0}));
//Dereference preparation
const bool flagPrepareDerefence = std::any_of(typVariant->__operands.begin(), typVariant->__operands.end(), [](const TypeAnnotation& op){
return op.isValid();
});
llvm::Value* addrAsStorage = nullptr;
if (flagPrepareDerefence){
assert(exprSwitch.bindings.size() && "Switch condition alias not found");
llvm::Type* typStorageRaw = llvm::cast<llvm::StructType>(typVariantRaw)->getElementType(1);
llvm::Value* storageRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef<unsigned>({1}));
addrAsStorage = llvm->irBuilder.CreateAlloca(typStorageRaw);
llvm->irBuilder.CreateStore(storageRaw, addrAsStorage);
}
llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(idRaw, nullptr, casesCount);
llvm::BasicBlock* blockDefaultUndefined;
std::list<CodeScope*>::const_iterator scopeCaseIt = exprSwitch.blocks.begin();
for (int instancesSize = exprSwitch.operands.size()-1, instId = 0; instId < instancesSize; ++instId) {
llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(instId), function->raw);
builder.SetInsertPoint(blockCase);
IBruteScope* unitCase = function->getBruteScope(*scopeCaseIt);
const ExpandedType& instType = ExpandedType(typVariant->__operands.at(instId));
//Actual variant derefence
if (instType->isValid()) {
string identCondition = exprSwitch.bindings.front();
llvm::Type* instTypeRaw = llvm->toLLVMType(instType);
llvm::Value* addrAsInst = llvm->irBuilder.CreateBitOrPointerCast(addrAsStorage, instTypeRaw->getPointerTo());
llvm::Value* instRaw = llvm->irBuilder.CreateLoad(instTypeRaw, addrAsInst);
const Symbol& identSymb = unitCase->bindArg(instRaw, move(identCondition));
Attachments::put<TypeInferred>(identSymb, instType);
}
llvm::Value* resultCase = function->getBruteScope(*scopeCaseIt)->compile();
builder.CreateBr(blockEpilog);
ret->addIncoming(resultCase, blockDefaultUndefined = builder.GetInsertBlock());
builder.SetInsertPoint(blockProlog);
instructionSwitch->addCase(dyn_cast<llvm::ConstantInt>(llvm::ConstantInt::get(typI8, exprSwitch.operands.at(instId+1).getValueDouble())), blockCase);
++scopeCaseIt;
}
instructionSwitch->setDefaultDest(blockDefaultUndefined);
builder.SetInsertPoint(blockEpilog);
return ret;
}
llvm::Value*
ControlIR::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) {
EXPAND_CONTEXT UNUSED(function); UNUSED(scope);
Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm->llvmContext));
//ArrayType* typStr = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::I8, size+1))));
/*
std::vector<Constant *> chars;
chars.reserve(size+1);
for (size_t i=0; i< size; ++i){
chars[i] = ConstantInt::get(typI8, (unsigned char) data[i]);
}
chars[size] = ConstantInt::get(typI8, 0);
*/
Value* rawData = ConstantDataArray::getString(llvm->llvmContext, data);
Value* rawPtrData = llvm->irBuilder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 1, false));
llvm->irBuilder.CreateStore(rawData, rawPtrData);
return llvm->irBuilder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar);
}
llvm::Value*
ControlIR::compileSequence(const Expression &expr){
EXPAND_CONTEXT UNUSED(scope); UNUSED(llvm);
llvm::Value* result;
for(CodeScope* scope: expr.blocks){
result = function->getBruteScope(scope)->compile();
}
return result;
}
diff --git a/cpp/src/compilation/decorators.h b/cpp/src/compilation/decorators.h
index 4e78236..61b4f65 100644
--- a/cpp/src/compilation/decorators.h
+++ b/cpp/src/compilation/decorators.h
@@ -1,237 +1,254 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: scopedecorators.h
* Author: pgess <v.melnychenko@xreate.org>
*
* Created on February 24, 2017, 11:35 AM
*/
/**
* \file scopedecorators.h
* \brief Basic code block compilation xreate::compilation::IBruteScope decorators
*/
#ifndef SCOPEDECORATORS_H
#define SCOPEDECORATORS_H
#include "ast.h"
#include "compilation/transformations.h"
#include "analysis/typeinference.h"
#include "compilation/demand.h"
#include "compilation/polymorph.h"
#include "compilation/targetinterpretation.h"
#ifndef XREATE_CONFIG_MIN
#include "compilation/versions.h"
#include "compilation/polymorph.h"
#endif
#include <list>
namespace xreate {
class CompilePass;
namespace compilation {
class IBruteScope;
class IBruteFunction;
/**\brief Provides caching ability for code scope compilation
* \extends xreate::compilation::IBruteScope
*/
template<class Parent>
class CachedScopeDecorator: public Parent{
- typedef CachedScopeDecorator<Parent> SELF;
+ typedef CachedScopeDecorator<Parent> SELF;
public:
- CachedScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}
-
- Symbol bindArg(llvm::Value* value, std::string&& alias)
- {
- //ensure existence of an alias
- assert(Parent::scope->__identifiers.count(alias));
-
- //memorize new value for an alias
- ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE};
- __rawVars[id] = value;
-
- return Symbol{id, Parent::scope};
+ CachedScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}
+
+ Symbol
+ bindArg(llvm::Value* value, std::string&& alias)
+ {
+ //ensure existence of an alias
+ assert(Parent::scope->__identifiers.count(alias));
+
+ //memorize new value for an alias
+ ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE};
+ __rawVars[id] = value;
+
+ return Symbol{id, Parent::scope};
+ }
+
+ void
+ bindArg(llvm::Value* value, const ScopedSymbol& s) override{
+ __rawVars[s] = value;
+ }
+
+ void
+ bindExternalSymb(llvm::Value* value, const Symbol& s) override {
+ __rawExternals[s] = value;
+ }
+
+ llvm::Value*
+ compile(const std::string& aliasBlock="") override{
+ if (__rawVars.count(ScopedSymbol::RetSymbol)){
+ return __rawVars[ScopedSymbol::RetSymbol];
}
- void bindArg(llvm::Value* value, const ScopedSymbol& s) {
- __rawVars[s] = value;
- }
+ return Parent::compile(aliasBlock);
+ }
- llvm::Value* compile(const std::string& aliasBlock="") override{
- if (__rawVars.count(ScopedSymbol::RetSymbol)){
- return __rawVars[ScopedSymbol::RetSymbol];
+ llvm::Value*
+ processSymbol(const Symbol& s, std::string hintRetVar) override{
+ if (Parent::function->isLambda){
+ SELF* self = dynamic_cast<SELF*>(Parent::function->getEntry());
+ if (self->__rawExternals.count(s)){
+ return self->__rawExternals.at(s);
}
-
- return Parent::compile(aliasBlock);
}
- llvm::Value*
- processSymbol(const Symbol& s, std::string hintRetVar) override{
- const CodeScope* scope = s.scope;
- SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(scope));
+ const CodeScope* scope = s.scope;
+ SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(scope));
- if (self->__rawVars.count(s.identifier)){
- return self->__rawVars[s.identifier];
- }
+ if (self->__rawVars.count(s.identifier)){
+ return self->__rawVars.at(s.identifier);
+ }
- //Declaration could be overriden
- /*
- Expression declaration = CodeScope::getDefinition(s, true);
- if (!declaration.isDefined()){
- assert(__declarationsOverriden.count(s.identifier));
- declaration = __declarationsOverriden[s.identifier];
+ //Declaration could be overriden
+ /*
+ Expression declaration = CodeScope::getDefinition(s, true);
+ if (!declaration.isDefined()){
+ assert(__declarationsOverriden.count(s.identifier));
+ declaration = __declarationsOverriden[s.identifier];
- } else {
- (false); //in case of binding there should be raws provided.
- }
+ } else {
+ (false); //in case of binding there should be raws provided.
}
- */
-
- llvm::Value* resultRaw = Parent::processSymbol(s, hintRetVar);
- self->__rawVars.emplace(s.identifier, resultRaw);
- return resultRaw;
}
+ */
- void
- overrideDeclarations(std::list<std::pair<Symbol, Expression>> bindings){
- reset();
+ llvm::Value* resultRaw = Parent::processSymbol(s, hintRetVar);
+ self->__rawVars.emplace(s.identifier, resultRaw);
+ return resultRaw;
+ }
- for (auto entry: bindings){
- SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(entry.first.scope));
- assert(self == this);
+ void
+ overrideDeclarations(std::list<std::pair<Symbol, Expression>> bindings){
+ reset();
- self->__declarationsOverriden.emplace(entry.first.identifier, entry.second);
- }
- }
+ for (auto entry: bindings){
+ SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(entry.first.scope));
+ assert(self == this);
- void registerChildScope(std::shared_ptr<IBruteScope> scope){
- __childScopes.push_back(scope);
+ self->__declarationsOverriden.emplace(entry.first.identifier, entry.second);
}
+ }
- void reset(){
- __rawVars.clear();
- __declarationsOverriden.clear();
- __childScopes.clear();
- }
+ void registerChildScope(std::shared_ptr<IBruteScope> scope){
+ __childScopes.push_back(scope);
+ }
+
+ void reset(){
+ __rawVars.clear();
+ __declarationsOverriden.clear();
+ __childScopes.clear();
+ __rawExternals.clear();
+ }
private:
- std::unordered_map<ScopedSymbol, Expression> __declarationsOverriden;
- std::unordered_map<ScopedSymbol,llvm::Value*> __rawVars;
- std::list<std::shared_ptr<IBruteScope>> __childScopes;
+ std::unordered_map<ScopedSymbol, Expression> __declarationsOverriden;
+ std::unordered_map<ScopedSymbol,llvm::Value*> __rawVars;
+ std::unordered_map<Symbol,llvm::Value*> __rawExternals;
+ std::list<std::shared_ptr<IBruteScope>> __childScopes;
};
/** \brief Provides automatic type conversion
* \extends xreate::compilation::IBruteScope
*/
template<class Parent>
class TypeConversionScopeDecorator: public Parent {
public:
TypeConversionScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}
llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation()) override {
llvm::Value* resultR = Parent::process(expr, hintVarDecl, expectedT);
if(!expr.type.isValid()) {
return resultR;
}
ExpandedType exprT = Parent::pass->man->root->getType(expr);
llvm::Type* exprTR = Parent::pass->man->llvm->toLLVMType(exprT, expr);
return typeinference::doAutomaticTypeConversion(resultR, exprTR, Parent::pass->man->llvm->irBuilder);
}
};
#ifndef XREATE_CONFIG_MIN
/**\brief The default code scope compilation implementation
* \extends xreate::compilation::IBruteScope
*/
typedef
CachedScopeDecorator<
TypeConversionScopeDecorator<
latex::LatexBruteScopeDecorator<
polymorph::PolymorphBruteScopeDecorator<
compilation::TransformationsScopeDecorator<
interpretation::InterpretationScopeDecorator<
versions::VersionsScopeDecorator<
compilation::BasicBruteScope
>>>>>>>
DefaultCodeScopeUnit;
} //end of compilation namespace
struct CachedScopeDecoratorTag;
struct VersionsScopeDecoratorTag;
template<>
struct DecoratorsDict<CachedScopeDecoratorTag>{
typedef compilation::CachedScopeDecorator<
compilation::TypeConversionScopeDecorator<
latex::LatexBruteScopeDecorator<
polymorph::PolymorphBruteScopeDecorator<
compilation::TransformationsScopeDecorator<
interpretation::InterpretationScopeDecorator<
versions::VersionsScopeDecorator<
compilation::BasicBruteScope
>>>>>>>
result;
};
template<>
struct DecoratorsDict<VersionsScopeDecoratorTag>{
typedef
versions::VersionsScopeDecorator<
compilation::BasicBruteScope
>
result;
};
#else
/**\brief The default code scope compilation implementation
* \extends xreate::compilation::IBruteScope
*/
typedef
CachedScopeDecorator<
TypeConversionScopeDecorator<
interpretation::InterpretationScopeDecorator<
demand::DemandBruteScopeDecorator<
polymorph::PolymorphBruteScopeDecorator<
compilation::BasicBruteScope
>>>>>
DefaultCodeScopeUnit;
} //end of compilation namespacef
struct CachedScopeDecoratorTag;
template<>
struct DecoratorsDict<CachedScopeDecoratorTag>{
typedef compilation::CachedScopeDecorator<
compilation::TypeConversionScopeDecorator<
interpretation::InterpretationScopeDecorator<
demand::DemandBruteScopeDecorator<
polymorph::PolymorphBruteScopeDecorator<
compilation::BasicBruteScope
>>>>>
result;
};
typedef
demand::DemandBruteFnDecorator<
//polymorph::PolymorphBruteFnDecorator<
compilation::BasicBruteFunction
> BruteFunctionDefault;
#endif
} //end of xreate
#endif /* SCOPEDECORATORS_H */
diff --git a/cpp/src/compilation/lambdas.cpp b/cpp/src/compilation/lambdas.cpp
index b82f971..0bed041 100644
--- a/cpp/src/compilation/lambdas.cpp
+++ b/cpp/src/compilation/lambdas.cpp
@@ -1,65 +1,71 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: lambdas.cpp
* Author: pgess <v.melnychenko@xreate.org>
*
* Created in April, 2020
*/
#include "compilation/lambdas.h"
#include "llvmlayer.h"
#include "compilation/resources.h"
using namespace xreate::compilation;
using namespace std;
unsigned LambdaBruteFn::__counter = 0;
std::string
LambdaBruteFn::prepareName(){
return string(LAMBDA_PREFIX) + "_" + __hintAlias + "_" + to_string(__counter++);
}
std::vector<llvm::Type*>
LambdaBruteFn::prepareSignature(){
return getScopeSignature(IBruteFunction::__entry);
}
llvm::Type*
LambdaBruteFn::prepareResult(){
LLVMLayer* llvm = IBruteFunction::pass->man->llvm;
AST* ast = IBruteFunction::pass->man->root;
return llvm->toLLVMType(ast->getType(__entry->getBody()));
}
llvm::Function::arg_iterator
LambdaBruteFn::prepareBindings(){
- CodeScope* entry = IBruteFunction::__entry;
- IBruteScope* entryCompilation = IBruteFunction::getBruteScope(entry);
+ CodeScope* entrySc = IBruteFunction::__entry;
+ IBruteScope* entryBruteSc = IBruteFunction::getBruteScope(entrySc);
llvm::Function::arg_iterator fargsI = IBruteFunction::raw->arg_begin();
- for (std::string &arg : entry->__bindings) {
- ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE};
+ for (std::string &arg : entrySc->__bindings) {
+ ScopedSymbol argid{entrySc->__identifiers[arg], versions::VERSION_NONE};
- entryCompilation->bindArg(&*fargsI, argid);
+ entryBruteSc->bindArg(&*fargsI, argid);
fargsI->setName(arg);
++fargsI;
}
+ for (Symbol symbExtern: entrySc->boundExternalSymbs){
+ entryBruteSc->bindExternalSymb(&*fargsI, symbExtern);
+ //fargsI->setName(arg);
+ ++fargsI;
+ }
+
return fargsI;
}
void
LambdaBruteFn::applyAttributes(){
raw->addFnAttr(llvm::Attribute::AlwaysInline);
}
llvm::Function*
LambdaIR::compile(CodeScope* body, const std::string& hintAlias){
LambdaBruteFn fnLambda(body, __pass, hintAlias);
return fnLambda.compile();
}
diff --git a/cpp/src/compilation/lambdas.h b/cpp/src/compilation/lambdas.h
index 900b151..2f3f355 100644
--- a/cpp/src/compilation/lambdas.h
+++ b/cpp/src/compilation/lambdas.h
@@ -1,52 +1,55 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: lambdas.h
* Author: pgess <v.melnychenko@xreate.org>
*
* Created in April, 2020
*/
#ifndef XREATE_LAMBDAS_H
#define XREATE_LAMBDAS_H
#include "pass/compilepass.h"
namespace llvm {
class Function;
}
namespace xreate { namespace compilation {
class LambdaBruteFn: public IBruteFunction {
public:
LambdaBruteFn(CodeScope* entry, CompilePass* p, const std::string& hintAlias)
- : IBruteFunction(entry, p), __hintAlias(hintAlias) {}
+ : IBruteFunction(entry, p), __hintAlias(hintAlias) {
+ IBruteFunction::isLambda = true;
+ }
-protected:
virtual std::string prepareName() override;
+
+protected:
virtual std::vector<llvm::Type*> prepareSignature() override;
virtual llvm::Function::arg_iterator prepareBindings() override;
virtual llvm::Type* prepareResult() override;
virtual void applyAttributes() override;
private:
std::string __hintAlias;
static unsigned __counter;
};
class LambdaIR{
public:
LambdaIR(CompilePass* p): __pass(p){}
llvm::Function* compile(CodeScope* body, const std::string& hintAlias);
private:
compilation::Context __context;
CompilePass* __pass;
};
}}
#endif //XREATE_LAMBDAS_H
diff --git a/cpp/src/compilation/targetinterpretation.cpp b/cpp/src/compilation/targetinterpretation.cpp
index 45605dd..d6f93b9 100644
--- a/cpp/src/compilation/targetinterpretation.cpp
+++ b/cpp/src/compilation/targetinterpretation.cpp
@@ -1,646 +1,646 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: targetinterpretation.cpp
* Author: pgess <v.melnychenko@xreate.org>
*
* Created on June 29, 2016, 6:45 PM
*/
/**
* \file targetinterpretation.h
* \brief Interpretation support. See more details on [Interpretation](/d/concepts/interpretation/)
*/
#include "compilation/targetinterpretation.h"
#include "pass/interpretationpass.h"
#include "analysis/typeinference.h"
#include "llvmlayer.h"
#include "compilation/decorators.h"
#include "compilation/i12ninst.h"
#include "compilation/intrinsics.h"
#include <boost/scoped_ptr.hpp>
#include <iostream>
#include <csignal>
using namespace std;
using namespace xreate::compilation;
namespace xreate{
namespace interpretation{
const Expression EXPRESSION_FALSE = Expression(Atom<Number_t>(0));
const Expression EXPRESSION_TRUE = Expression(Atom<Number_t>(1));
CodeScope*
InterpretationScope::processOperatorIf(const Expression& expression) {
const Expression& exprCondition = process(expression.getOperands()[0]);
if (exprCondition == EXPRESSION_TRUE) {
return expression.blocks.front();
}
return expression.blocks.back();
}
CodeScope*
InterpretationScope::processOperatorSwitch(const Expression& expression) {
const Expression& exprCondition = process(expression.operands[0]);
bool flagHasDefault = expression.operands[1].op == Operator::CASE_DEFAULT;
//TODO check that one and only one case variant is appropriate
for (size_t size = expression.operands.size(), i = flagHasDefault ? 2 : 1; i < size; ++i) {
const Expression& exprCase = process(expression.operands[i]);
if (function->getScope((const CodeScope*) exprCase.blocks.front())->processScope() == exprCondition) {
return exprCase.blocks.back();
}
}
if (flagHasDefault) {
const Expression& exprCaseDefault = expression.operands[1];
return exprCaseDefault.blocks.front();
}
assert(false && "Switch has no appropriate variant");
return nullptr;
}
CodeScope*
InterpretationScope::processOperatorSwitchVariant(const Expression& expression) {
const ExpandedType& conditionT = function->__pass->man->root->getType(expression.operands.at(0));
const Expression& conditionE = process(expression.operands.at(0));
assert(conditionE.op == Operator::VARIANT);
const string& aliasS = expression.bindings.front();
unsigned caseExpectedId = (int) conditionE.getValueDouble();
auto itFoundValue = std::find_if(++expression.operands.begin(), expression.operands.end(), [caseExpectedId](const auto& caseActualE){
return (unsigned) caseActualE.getValueDouble() == caseExpectedId;
});
assert(itFoundValue != expression.operands.end());
int caseScopeId = itFoundValue - expression.operands.begin() - 1;
auto caseScopeRef = expression.blocks.begin();
std::advance(caseScopeRef, caseScopeId);
InterpretationScope* scopeI12n = function->getScope(*caseScopeRef);
if(conditionE.operands.size()) {
Expression valueE(Operator::LIST, {});
valueE.operands = conditionE.operands;
valueE.bindings = conditionT->__operands.at(caseExpectedId).fields;
scopeI12n->overrideBindings({
{valueE, aliasS}
});
};
return *caseScopeRef;
}
llvm::Value*
InterpretationScope::processLate(const InterpretationOperator& op, const Expression& expression, const Context& context, const std::string& hintAlias) {
switch(op) {
case IF_INTERPRET_CONDITION:
{
CodeScope* scopeResult = processOperatorIf(expression);
llvm::Value* result = context.function->getBruteScope(scopeResult)->compile();
return result;
}
case SWITCH_INTERPRET_CONDITION:
{
CodeScope* scopeResult = processOperatorSwitch(expression);
llvm::Value* result = context.function->getBruteScope(scopeResult)->compile();
return result;
}
case SWITCH_VARIANT:
{
CodeScope* scopeResult = processOperatorSwitchVariant(expression);
const Expression& condCrudeE = expression.operands.at(0);
const Expression& condE = process(condCrudeE);
const string identCondition = expression.bindings.front();
auto scopeCompilation = Decorators<CachedScopeDecoratorTag>::getInterface(
context.function->getBruteScope(scopeResult));
if(condE.operands.size()) {
//override value
Symbol symbCondition{ScopedSymbol{scopeResult->__identifiers.at(identCondition), versions::VERSION_NONE}, scopeResult};
scopeCompilation->overrideDeclarations({
{symbCondition, Expression(condE.operands.at(0))}}
);
//set correct type for binding:
const ExpandedType& typeVariant = function->__pass->man->root->getType(condCrudeE);
int conditionIndex = condE.getValueDouble();
- ScopedSymbol symbolInternal = scopeResult->getSymbol(identCondition);
+ ScopedSymbol symbolInternal = scopeResult->findSymbolByAlias(identCondition);
scopeResult->__declarations[symbolInternal].bindType(typeVariant->__operands.at(conditionIndex));
}
llvm::Value* result = context.function->getBruteScope(scopeResult)->compile();
return result;
}
case SWITCH_LATE:
{
return nullptr;
// latereasoning::LateReasoningCompiler compiler(dynamic_cast<InterpretationFunction*>(this->function), context);
// return compiler.processSwitchLateStatement(expression, "");
}
case FOLD_INTERPRET_INPUT:
{
//initialization
const Expression& containerE = process(expression.getOperands().at(0));
const TypeAnnotation& accumT = expression.type;
assert(containerE.op == Operator::LIST);
CodeScope* bodyScope = expression.blocks.front();
const string& elAlias = expression.bindings[0];
Symbol elS{ScopedSymbol{bodyScope->__identifiers.at(elAlias), versions::VERSION_NONE}, bodyScope};
const std::string& accumAlias = expression.bindings[1];
llvm::Value* accumRaw = context.scope->process(expression.getOperands().at(1), accumAlias, accumT);
InterpretationScope* bodyI12n = function->getScope(bodyScope);
auto bodyBrute = Decorators<CachedScopeDecoratorTag>::getInterface(context.function->getBruteScope(bodyScope));
const std::vector<Expression>& containerVec = containerE.getOperands();
for(size_t i = 0; i < containerVec.size(); ++i) {
const Expression& elE = containerVec[i];
bodyI12n->overrideBindings({
{elE, elAlias}
});
bodyBrute->overrideDeclarations({
{elS, elE}
}); //resets bodyBrute
bodyBrute->bindArg(accumRaw, string(accumAlias));
accumRaw = bodyBrute->compile();
}
return accumRaw;
}
// case FOLD_INF_INTERPRET_INOUT:
// {
// }
//TODO refactor as InterpretationCallStatement class
case CALL_INTERPRET_PARTIAL:
{
const std::string &calleeName = expression.getValueString();
IBruteScope* scopeUnitSelf = context.scope;
ManagedFnPtr callee = this->function->__pass->man->root->findFunction(calleeName);
const I12nFunctionSpec& calleeData = FunctionInterpretationHelper::getSignature(callee);
std::vector<llvm::Value *> argsActual;
PIFnSignature sig;
sig.declaration = callee;
for(size_t no = 0, size = expression.operands.size(); no < size; ++no) {
const Expression& op = expression.operands[no];
if (calleeData.signature.at(no) == INTR_ONLY) {
sig.bindings.push_back(process(op));
continue;
}
argsActual.push_back(scopeUnitSelf->process(op));
}
TargetInterpretation* man = dynamic_cast<TargetInterpretation*> (this->function->__pass);
PIFunction* pifunction = man->getFunction(move(sig));
llvm::Function* raw = pifunction->compile();
boost::scoped_ptr<BruteFnInvocation> statement(new BruteFnInvocation(raw, man->pass->man->llvm));
return (*statement)(move(argsActual));
}
case QUERY_LATE:
{
return nullptr;
// return IntrinsicQueryInstruction(
// dynamic_cast<InterpretationFunction*>(this->function))
// .processLate(expression, context);
}
default: break;
}
assert(false && "Unknown late interpretation operator");
return nullptr;
}
llvm::Value*
InterpretationScope::compile(const Expression& expression, const Context& context, const std::string& hintAlias) {
const InterpretationData& data = Attachments::get<InterpretationData>(expression);
if (data.op != InterpretationOperator::NONE) {
return processLate(data.op, expression, context, hintAlias);
}
Expression result = process(expression);
return context.scope->process(result, hintAlias);
}
Expression
InterpretationScope::process(const Expression& expression) {
#ifndef NDEBUG
if (expression.tags.count("bpoint")) {
std::raise(SIGINT);
}
#endif
PassManager* man = function->__pass->man;
switch (expression.__state) {
case Expression::INVALID:
assert(false);
case Expression::NUMBER:
case Expression::STRING:
return expression;
case Expression::IDENT:
{
Symbol s = Attachments::get<IdentifierSymbol>(expression);
return Parent::processSymbol(s);
}
case Expression::COMPOUND:
break;
default: assert(false);
}
switch (expression.op) {
case Operator::EQU:
{
const Expression& left = process(expression.operands[0]);
const Expression& right = process(expression.operands[1]);
if (left == right) return EXPRESSION_TRUE;
return EXPRESSION_FALSE;
}
case Operator::NE:
{
const Expression& left = process(expression.operands[0]);
const Expression& right = process(expression.operands[1]);
if (left == right) return EXPRESSION_FALSE;
return EXPRESSION_TRUE;
}
case Operator::LOGIC_AND:
{
assert(expression.operands.size() == 1);
return process (expression.operands[0]);
}
// case Operator::LOGIC_OR:
case Operator::CALL:
{
const std::string &fnName = expression.getValueString();
ManagedFnPtr fnAst = man->root->findFunction(fnName);
InterpretationFunction* fnUnit = this->function->__pass->getFunction(fnAst);
vector<Expression> args;
args.reserve(expression.getOperands().size());
for(size_t i = 0, size = expression.getOperands().size(); i < size; ++i) {
args.push_back(process(expression.getOperands()[i]));
}
return fnUnit->process(args);
}
case Operator::CALL_INTRINSIC:
{
const Expression& opCallIntrCrude = expression;
vector<Expression> argsActual;
argsActual.reserve(opCallIntrCrude.getOperands().size());
for(const auto& op: opCallIntrCrude.getOperands()) {
argsActual.push_back(process(op));
}
Expression opCallIntr(Operator::CALL_INTRINSIC, {});
opCallIntr.setValueDouble(opCallIntrCrude.getValueDouble());
opCallIntr.operands = argsActual;
compilation::IntrinsicCompiler compiler(man);
return compiler.interpret(opCallIntr);
}
case Operator::QUERY:
{
return Expression();
// return IntrinsicQueryInstruction(dynamic_cast<InterpretationFunction*>(this->function))
// .process(expression);
}
case Operator::QUERY_LATE:
{
assert(false && "Can't be interpretated");
return Expression();
}
case Operator::IF:
{
CodeScope* scopeResult = processOperatorIf(expression);
return function->getScope(scopeResult)->processScope();
}
case Operator::SWITCH:
{
CodeScope* scopeResult = processOperatorSwitch(expression);
return function->getScope(scopeResult)->processScope();
}
case Operator::SWITCH_VARIANT:
{
CodeScope* scopeResult = processOperatorSwitchVariant(expression);
return function->getScope(scopeResult)->processScope();
}
case Operator::VARIANT:
{
Expression result{Operator::VARIANT, {}};
result.setValueDouble(expression.getValueDouble());
for(const Expression& op: expression.operands){
result.operands.push_back(process(op));
}
return result;
}
case Operator::INDEX:
{
Expression aggrE = process(expression.operands[0]);
for (size_t keyId = 1; keyId < expression.operands.size(); ++keyId) {
const Expression& keyE = process(expression.operands[keyId]);
if (keyE.__state == Expression::STRING) {
const string& fieldExpectedS = keyE.getValueString();
unsigned fieldId;
for(fieldId = 0; fieldId < aggrE.bindings.size(); ++fieldId){
if (aggrE.bindings.at(fieldId) == fieldExpectedS){break;}
}
assert(fieldId < aggrE.bindings.size());
aggrE = Expression(aggrE.operands.at(fieldId));
continue;
}
if (keyE.__state == Expression::NUMBER) {
int opId = keyE.getValueDouble();
aggrE = Expression(aggrE.operands.at(opId));
continue;
}
assert(false && "Inappropriate key");
}
return aggrE;
}
case Operator::FOLD:
{
const Expression& exprInput = process(expression.getOperands()[0]);
const Expression& exprInit = process(expression.getOperands()[1]);
const std::string& argEl = expression.bindings[0];
const std::string& argAccum = expression.bindings[1];
InterpretationScope* body = function->getScope(expression.blocks.front());
Expression accum = exprInit;
for(size_t size = exprInput.getOperands().size(), i = 0; i < size; ++i) {
body->overrideBindings({
{exprInput.getOperands()[i], argEl},
{accum, argAccum}
});
accum = body->processScope();
}
return accum;
}
case Operator::LIST:
case Operator::LIST_RANGE:
{
Expression result(expression.op,{});
result.operands.resize(expression.operands.size());
result.bindings = expression.bindings;
int keyId = 0;
for(const Expression& opCurrent : expression.operands) {
result.operands[keyId++] = process(opCurrent);
}
return result;
}
// case Operator::MAP: {
// break;
// }
default: break;
}
return expression;
}
InterpretationFunction*
TargetInterpretation::getFunction(IBruteFunction* unit) {
if (__dictFunctionsByUnit.count(unit)) {
return __dictFunctionsByUnit.at(unit);
}
InterpretationFunction* f = new InterpretationFunction(unit->getASTFn(), this);
__dictFunctionsByUnit.emplace(unit, f);
assert(__functions.emplace(unit->getASTFn().id(), f).second);
return f;
}
PIFunction*
TargetInterpretation::getFunction(PIFnSignature&& sig) {
auto f = __pifunctions.find(sig);
if (f != __pifunctions.end()) {
return f->second;
}
PIFunction* result = new PIFunction(PIFnSignature(sig), __pifunctions.size(), this);
__pifunctions.emplace(move(sig), result);
assert(__dictFunctionsByUnit.emplace(result->fnRaw, result).second);
return result;
}
InterpretationScope*
TargetInterpretation::transformContext(const Context& c) {
return this->getFunction(c.function)->getScope(c.scope->scope);
}
llvm::Value*
TargetInterpretation::compile(const Expression& expression, const Context& ctx, const std::string& hintAlias) {
return transformContext(ctx)->compile(expression, ctx, hintAlias);
}
InterpretationFunction::InterpretationFunction(const ManagedFnPtr& function, Target<TargetInterpretation>* target)
: Function<TargetInterpretation>(function, target) { }
Expression
InterpretationFunction::process(const std::vector<Expression>& args) {
InterpretationScope* body = getScope(__function->__entry);
list<pair<Expression, string>> bindings;
for(size_t i = 0, size = args.size(); i < size; ++i) {
bindings.push_back(make_pair(args.at(i), body->scope->__bindings.at(i)));
}
body->overrideBindings(bindings);
return body->processScope();
}
// Partial function interpretation
typedef BasicBruteFunction BruteFunction;
class PIBruteFunction : public BruteFunction{
public:
PIBruteFunction(ManagedFnPtr f, std::set<size_t>&& arguments, size_t id, CompilePass* p)
: BruteFunction(f, p), argumentsActual(move(arguments)), __id(id) { }
+ virtual std::string
+ prepareName() override {
+ return BruteFunction::prepareName() + "_" + std::to_string(__id);
+ }
+
protected:
std::vector<llvm::Type*>
prepareSignature() override {
LLVMLayer* llvm = BruteFunction::pass->man->llvm;
AST* ast = BruteFunction::pass->man->root;
CodeScope* entry = IBruteFunction::__entry;
std::vector<llvm::Type*> signature;
for(size_t no : argumentsActual) {
VNameId argId = entry->__identifiers.at(entry->__bindings.at(no));
ScopedSymbol arg{argId, versions::VERSION_NONE};
signature.push_back(llvm->toLLVMType(ast->expandType(entry->__declarations.at(arg).type)));
}
return signature;
}
llvm::Function::arg_iterator
prepareBindings() override{
CodeScope* entry = IBruteFunction::__entry;
IBruteScope* entryCompilation = BruteFunction::getBruteScope(entry);
llvm::Function::arg_iterator fargsI = BruteFunction::raw->arg_begin();
for(size_t no : argumentsActual) {
ScopedSymbol arg{entry->__identifiers.at(entry->__bindings.at(no)), versions::VERSION_NONE};
entryCompilation->bindArg(&*fargsI, arg);
fargsI->setName(entry->__bindings.at(no));
++fargsI;
}
return fargsI;
}
- virtual std::string
- prepareName() override {
- return BruteFunction::prepareName() + "_" + std::to_string(__id);
- }
-
private:
std::set<size_t> argumentsActual;
size_t __id;
} ;
PIFunction::PIFunction(PIFnSignature&& sig, size_t id, TargetInterpretation* target)
: InterpretationFunction(sig.declaration, target), instance(move(sig)) {
const I12nFunctionSpec& functionData = FunctionInterpretationHelper::getSignature(instance.declaration);
std::set<size_t> argumentsActual;
for (size_t no = 0, size = functionData.signature.size(); no < size; ++no) {
if (functionData.signature.at(no) != INTR_ONLY) {
argumentsActual.insert(no);
}
}
fnRaw = new PIBruteFunction(instance.declaration, move(argumentsActual), id, target->pass);
CodeScope* entry = instance.declaration->__entry;
auto entryUnit = Decorators<CachedScopeDecoratorTag>::getInterface<>(fnRaw->getEntry());
InterpretationScope* entryIntrp = InterpretationFunction::getScope(entry);
list<pair<Expression, std::string>> bindingsPartial;
list<pair<Symbol, Expression>> declsPartial;
for(size_t no = 0, sigNo = 0, size = entry->__bindings.size(); no < size; ++no) {
if(functionData.signature.at(no) == INTR_ONLY) {
bindingsPartial.push_back({instance.bindings[sigNo], entry->__bindings[no]});
VNameId argId = entry->__identifiers.at(entry->__bindings[no]);
Symbol argSymbol{ScopedSymbol
{argId, versions::VERSION_NONE}, entry};
declsPartial.push_back({argSymbol, instance.bindings[sigNo]});
++sigNo;
}
}
entryIntrp->overrideBindings(bindingsPartial);
entryUnit->overrideDeclarations(declsPartial);
}
llvm::Function*
PIFunction::compile() {
llvm::Function* raw = fnRaw->compile();
return raw;
}
bool operator<(const PIFnSignature& lhs, const PIFnSignature& rhs) {
if (lhs.declaration.id() != rhs.declaration.id()) {
return lhs.declaration.id() < rhs.declaration.id();
}
return lhs.bindings < rhs.bindings;
}
bool operator<(const PIFnSignature& lhs, PIFunction * const rhs) {
return lhs < rhs->instance;
}
bool operator<(PIFunction * const lhs, const PIFnSignature& rhs) {
return lhs->instance < rhs;
}
}
}
/** \class xreate::interpretation::InterpretationFunction
*
* Holds list of xreate::interpretation::InterpretationScope 's focused on interpretation of individual code scopes
*
* There is particulat subclass PIFunction intended to represent partially interpreted functions.
*\sa TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/)
*/
/** \class xreate::interpretation::TargetInterpretation
*
* TargetInterpretation is executed during compilation and is intended to preprocess eligible for interpretation parts of a source code.
*
* Keeps a list of InterpretationFunction / PIFunction that represent interpretation for an individual functions.
*
* There is \ref InterpretationScopeDecorator that embeds interpretation to an overall compilation process.
* \sa InterpretationPass, compilation::Target, [Interpretation Concept](/d/concepts/interpretation/)
*
*/
diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp
index 4eb8b7f..cdd9647 100644
--- a/cpp/src/pass/compilepass.cpp
+++ b/cpp/src/pass/compilepass.cpp
@@ -1,895 +1,908 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: pgess <v.melnychenko@xreate.org>
*/
/**
* \file compilepass.h
* \brief Main compilation routine. See \ref xreate::CompilePass
*/
#include "compilepass.h"
#include "transcendlayer.h"
#include "ast.h"
#include "llvmlayer.h"
#include "compilation/decorators.h"
#include "compilation/pointers.h"
#include "analysis/typeinference.h"
#include "compilation/control.h"
#include "compilation/demand.h"
#include "analysis/resources.h"
#ifdef XREATE_ENABLE_EXTERN
#include "ExternLayer.h"
#endif
#include "compilation/containers.h"
#include "compilation/containers/arrays.h"
#ifndef XREATE_CONFIG_MIN
#include "query/containers.h"
#include "pass/versionspass.h"
#include "compilation/targetinterpretation.h"
#endif
#include <boost/optional.hpp>
#include <memory>
using namespace std;
using namespace llvm;
using namespace xreate::typehints;
using namespace xreate::containers;
namespace xreate{
namespace compilation{
#define DEFAULT(x) (hintAlias.empty()? x: hintAlias)
std::string
BasicBruteFunction::prepareName() {
AST* ast = IBruteFunction::pass->man->root;
string name = ast->getFnSpecializations(__function->__name).size() > 1 ?
__function->__name + std::to_string(__function.id()) :
__function->__name;
return name;
}
std::vector<llvm::Type*>
BasicBruteFunction::prepareSignature() {
CodeScope* entry = __function->__entry;
return getScopeSignature(entry);
}
llvm::Type*
BasicBruteFunction::prepareResult() {
LLVMLayer* llvm = IBruteFunction::pass->man->llvm;
AST* ast = IBruteFunction::pass->man->root;
CodeScope* entry = __function->__entry;
return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type));
}
llvm::Function::arg_iterator
BasicBruteFunction::prepareBindings() {
CodeScope* entry = __function->__entry;
IBruteScope* entryCompilation = IBruteFunction::getBruteScope(entry);
llvm::Function::arg_iterator fargsI = IBruteFunction::raw->arg_begin();
for (std::string &arg : entry->__bindings) {
ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE};
entryCompilation->bindArg(&*fargsI, argid);
fargsI->setName(arg);
++fargsI;
}
return fargsI;
}
void
BasicBruteFunction::applyAttributes(){}
IBruteScope::IBruteScope(const CodeScope * const codeScope, IBruteFunction* f, CompilePass* compilePass)
: pass(compilePass), function(f), scope(codeScope), lastBlockRaw(nullptr) { }
llvm::Value*
BruteFnInvocation::operator()(std::vector<llvm::Value *>&& args, const std::string& hintDecl) {
if (__calleeTy) {
auto argsFormalT = __calleeTy->params();
size_t sizeArgsF = __calleeTy->getNumParams();
assert(args.size() >= sizeArgsF);
assert(__calleeTy->isVarArg() || args.size() == sizeArgsF);
auto argFormalT = argsFormalT.begin();
for(size_t argId = 0; argId < args.size(); ++argId){
if(argFormalT != argsFormalT.end()){
args[argId] = typeinference::doAutomaticTypeConversion(
args.at(argId), *argFormalT, llvm->irBuilder);
++argFormalT;
}
}
}
//Do not name function call that returns Void.
std::string hintName = (!__calleeTy->getReturnType()->isVoidTy()) ? hintDecl : "";
return llvm->irBuilder.CreateCall(__calleeTy, __callee, args, hintName);
}
llvm::Value*
HiddenArgsFnInvocation::operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl) {
args.insert(args.end(), __args.begin(), __args.end());
return __parent->operator ()(std::move(args), hintDecl);
}
class CallStatementInline : public IFnInvocation{
public:
CallStatementInline(IBruteFunction* caller, IBruteFunction* callee, LLVMLayer* l)
: __caller(caller), __callee(callee), llvm(l) { }
llvm::Value* operator()(std::vector<llvm::Value *>&& args, const std::string& hintDecl) {
return nullptr;
}
private:
IBruteFunction* __caller;
IBruteFunction* __callee;
LLVMLayer* llvm;
bool
isInline() {
// Symbol ret = Symbol{0, function->__entry};
// bool flagOnTheFly = SymbolAttachments::get<IsImplementationOnTheFly>(ret, false);
//TODO consider inlining
return false;
}
} ;
BasicBruteScope::BasicBruteScope(const CodeScope * const codeScope, IBruteFunction* f, CompilePass* compilePass)
: IBruteScope(codeScope, f, compilePass) { }
llvm::Value*
BasicBruteScope::processSymbol(const Symbol& s, std::string hintRetVar) {
Expression declaration = CodeScope::getDefinition(s);
const CodeScope* scopeExternal = s.scope;
IBruteScope* scopeBruteExternal = IBruteScope::function->getBruteScope(scopeExternal);
assert(scopeBruteExternal->lastBlockRaw);
llvm::Value* resultRaw;
llvm::BasicBlock* blockOwn = pass->man->llvm->irBuilder.GetInsertBlock();
if (scopeBruteExternal->lastBlockRaw == blockOwn) {
resultRaw = scopeBruteExternal->process(declaration, hintRetVar);
scopeBruteExternal->lastBlockRaw = lastBlockRaw =
pass->man->llvm->irBuilder.GetInsertBlock();
} else {
pass->man->llvm->irBuilder.SetInsertPoint(scopeBruteExternal->lastBlockRaw);
resultRaw = scopeBruteExternal->processSymbol(s, hintRetVar);
pass->man->llvm->irBuilder.SetInsertPoint(blockOwn);
}
return resultRaw;
}
IFnInvocation*
BasicBruteScope::findFunction(const Expression& opCall) {
const std::string& calleeName = opCall.getValueString();
LLVMLayer* llvm = pass->man->llvm;
const std::list<ManagedFnPtr>& specializations = pass->man->root->getFnSpecializations(calleeName);
#ifdef XREATE_ENABLE_EXTERN
//if no specializations registered - check external function
if (specializations.size() == 0) {
llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName);
llvm::outs() << "Debug/External function: " << calleeName;
external->getType()->print(llvm::outs(), true);
llvm::outs() << "\n";
return new BruteFnInvocation(external, llvm);
}
#endif
//There should be only one specialization without any valid guards at this point
return new BruteFnInvocation(pass->getBruteFn(
pass->man->root->findFunction(calleeName))->compile(),
llvm);
}
//DISABLEDFEATURE transformations
// if (pass->transformations->isAcceptable(expr)){
// return pass->transformations->transform(expr, result, ctx);
// }
llvm::Value*
BasicBruteScope::process(const Expression& expr, const std::string& hintAlias, const TypeAnnotation& expectedT) {
llvm::Value *leftRaw;
llvm::Value *rightRaw;
LLVMLayer& l = *pass->man->llvm;
Context ctx{this, function, pass};
xreate::compilation::ControlIR controlIR = xreate::compilation::ControlIR({this, function, pass});
switch (expr.op) {
case Operator::ADD:
case Operator::SUB: case Operator::MUL: case Operator::MOD:
case Operator::DIV: case Operator::EQU: case Operator::LSS:
case Operator::GTR: case Operator::NE: case Operator::LSE:
case Operator::GTE:
assert(expr.__state == Expression::COMPOUND);
assert(expr.operands.size() == 2);
leftRaw = process(expr.operands.at(0));
rightRaw = process(expr.operands.at(1));
break;
default:;
}
switch (expr.op) {
case Operator::AND:
{
assert(expr.operands.size());
llvm::Value* resultRaw = process(expr.operands.at(0));
if (expr.operands.size() == 1) return resultRaw;
for(size_t i=1; i< expr.operands.size()-1; ++i){
resultRaw = l.irBuilder.CreateAnd(resultRaw, process(expr.operands.at(i)));
}
return l.irBuilder.CreateAnd(resultRaw, process(expr.operands.at(expr.operands.size()-1)), hintAlias);
}
case Operator::OR:
{
assert(expr.operands.size());
llvm::Value* resultRaw = process(expr.operands.at(0));
if (expr.operands.size() == 1) return resultRaw;
for(size_t i=1; i< expr.operands.size()-1; ++i){
resultRaw = l.irBuilder.CreateOr(resultRaw, process(expr.operands.at(i)));
}
return l.irBuilder.CreateOr(resultRaw, process(expr.operands.at(expr.operands.size()-1)), hintAlias);
}
case Operator::ADD:
{
- return l.irBuilder.CreateAdd(leftRaw, rightRaw, DEFAULT("addv"));
+ return l.irBuilder.CreateAdd(leftRaw, rightRaw, hintAlias);
}
case Operator::SUB:
- return l.irBuilder.CreateSub(leftRaw, rightRaw, DEFAULT("tmp_sub"));
+ return l.irBuilder.CreateSub(leftRaw, rightRaw, hintAlias);
break;
case Operator::MUL:
- return l.irBuilder.CreateMul(leftRaw, rightRaw, DEFAULT("tmp_mul"));
+ return l.irBuilder.CreateMul(leftRaw, rightRaw, hintAlias);
break;
case Operator::DIV:
- if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateSDiv(leftRaw, rightRaw, DEFAULT("tmp_div"));
- if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFDiv(leftRaw, rightRaw, DEFAULT("tmp_div"));
+ if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateSDiv(leftRaw, rightRaw, hintAlias);
+ if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFDiv(leftRaw, rightRaw, hintAlias);
break;
case Operator::MOD:{
return l.irBuilder.CreateSRem(leftRaw, rightRaw, hintAlias);
}
case Operator::EQU: {
- if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateICmpEQ(leftRaw, rightRaw, DEFAULT("tmp_equ"));
- if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFCmpOEQ(leftRaw, rightRaw, DEFAULT("tmp_equ"));
+ if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateICmpEQ(leftRaw, rightRaw, hintAlias);
+ if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFCmpOEQ(leftRaw, rightRaw, hintAlias);
const ExpandedType& leftT = pass->man->root->getType(expr.operands[0]);
const ExpandedType& rightT = pass->man->root->getType(expr.operands[1]);
if(leftT->__operator == TypeOperator::VARIANT && rightT->__operator == TypeOperator::VARIANT){
llvm::Type* selectorT = llvm::cast<llvm::StructType>(leftRaw->getType())->getElementType(0);
llvm::Value* leftUnwapped = typeinference::doAutomaticTypeConversion(leftRaw, selectorT, l.irBuilder);
llvm::Value* rightUnwapped = typeinference::doAutomaticTypeConversion(rightRaw, selectorT, l.irBuilder);
- return l.irBuilder.CreateICmpEQ(leftUnwapped, rightUnwapped, DEFAULT("tmp_equ"));
+ return l.irBuilder.CreateICmpEQ(leftUnwapped, rightUnwapped, hintAlias);
}
break;
}
case Operator::NE:
- return l.irBuilder.CreateICmpNE(leftRaw, rightRaw, DEFAULT("tmp_ne"));
+ return l.irBuilder.CreateICmpNE(leftRaw, rightRaw, hintAlias);
break;
case Operator::LSS:
- return l.irBuilder.CreateICmpSLT(leftRaw, rightRaw, DEFAULT("tmp_lss"));
+ return l.irBuilder.CreateICmpSLT(leftRaw, rightRaw, hintAlias);
break;
case Operator::LSE:
- return l.irBuilder.CreateICmpSLE(leftRaw, rightRaw, DEFAULT("tmp_lse"));
+ return l.irBuilder.CreateICmpSLE(leftRaw, rightRaw, hintAlias);
break;
case Operator::GTR:
- return l.irBuilder.CreateICmpSGT(leftRaw, rightRaw, DEFAULT("tmp_gtr"));
+ return l.irBuilder.CreateICmpSGT(leftRaw, rightRaw, hintAlias);
break;
case Operator::GTE:
- return l.irBuilder.CreateICmpSGE(leftRaw, rightRaw, DEFAULT("tmp_gte"));
+ return l.irBuilder.CreateICmpSGE(leftRaw, rightRaw, hintAlias);
break;
case Operator::NEG:
{
leftRaw = process(expr.operands[0]);
ExpandedType leftTy = pass->man->root->getType(expr.operands[0]);
if (leftTy->__value == TypePrimitive::Bool){
return l.irBuilder.CreateNot(leftRaw, hintAlias);
} else {
return l.irBuilder.CreateNeg(leftRaw, hintAlias);
}
break;
}
case Operator::CALL:
{
assert(expr.__state == Expression::COMPOUND);
shared_ptr<IFnInvocation> callee(findFunction(expr));
const std::string& nameCallee = expr.getValueString();
//prepare arguments
std::vector<llvm::Value *> args;
args.reserve(expr.operands.size());
std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()),
[this](const Expression & operand) {
return process(operand);
}
);
- return (*callee)(move(args), DEFAULT("res_" + nameCallee));
+ return (*callee)(move(args), hintAlias);
}
case Operator::IF:
{
- return controlIR.compileIf(expr, DEFAULT("tmp_if"));
+ return controlIR.compileIf(expr, hintAlias);
}
case Operator::SWITCH:
{
- return controlIR.compileSwitch(expr, DEFAULT("tmp_switch"));
+ return controlIR.compileSwitch(expr, hintAlias);
}
case Operator::LOGIC_AND:
{
assert(expr.operands.size() == 1);
return process(expr.operands[0]);
}
case Operator::LIST: //init record or array
{
ExpandedType exprT = l.ast->getType(expr, expectedT);
TypesHelper helper(pass->man->llvm);
enum {RECORD, ARRAY} kind;
if (helper.isArrayT(exprT)){
kind = ARRAY;
} else if (helper.isRecordT(exprT)){
kind = RECORD;
} else {
assert(false && "Inapproriate type");
}
#ifdef XREATE_ENABLE_EXTERN
if (exprT->__operator == TypeOperator::ALIAS){
if (l.layerExtern->isArrayType(exprT->__valueCustom)){
flagIsArray = true;
break;
}
if (l.layerExtern->isRecordType(exprT->__valueCustom)){
flagIsArray = false;
break;
}
assert(false && "Inapproriate external type");
}
#endif
switch(kind){
case RECORD:{
const std::vector<string> fieldsFormal = helper.getRecordFields(exprT);
containers::RecordIR irRecords(ctx);
llvm::StructType *recordTRaw = llvm::cast<llvm::StructType>(l.toLLVMType(exprT));
llvm::Value *resultRaw = irRecords.init(recordTRaw);
return irRecords.update(resultRaw, exprT, expr);
}
case ARRAY: {
std::unique_ptr<containers::IContainersIR> containerIR(
containers::IContainersIR::create(expr, expectedT, ctx));
llvm::Value* aggrRaw = containerIR->init(hintAlias);
return containerIR->update(aggrRaw, expr, hintAlias);
}
}
break;
};
case Operator::LIST_RANGE:
{
containers::RangeIR compiler(ctx);
const ExpandedType& aggrT = pass->man->root->getType(expr);
return compiler.init(expr, aggrT, hintAlias);
};
case Operator::MAP:
{
assert(expr.blocks.size());
containers::ImplementationType implType = containers::IContainersIR::getImplementation(expr, pass->man->root);
switch(implType){
case containers::ImplementationType::SOLID: {
ExpandedType exprT = pass->man->root->getType(expr, expectedT);
ArrayHint hint = find(expr, ArrayHint{});
containers::ArrayIR compiler(exprT, hint, ctx);
- return compiler.operatorMap(expr, DEFAULT("map"));
+ return compiler.operatorMap(expr, hintAlias);
}
case containers::ImplementationType::ON_THE_FLY:{
FlyHint hint = find<FlyHint>(expr, {});
containers::FlyIR compiler(hint, ctx);
- return compiler.operatorMap(expr, DEFAULT("map"));
+ return compiler.operatorMap(expr, hintAlias);
}
default:
break;
}
assert(false && "Operator MAP does not support this container impl");
return nullptr;
};
case Operator::FOLD:
{
- return controlIR.compileFold(expr, DEFAULT("fold"));
+ return controlIR.compileFold(expr, hintAlias);
};
case Operator::FOLD_INF:
{
- return controlIR.compileFoldInf(expr, DEFAULT("fold"));
+ return controlIR.compileFoldInf(expr, hintAlias);
};
case Operator::INDEX:
{
assert(expr.operands.size() > 1);
const Expression& aggrE = expr.operands[0];
const ExpandedType& aggrT = pass->man->root->getType(aggrE);
llvm::Value* aggrRaw = process(aggrE);
switch (aggrT->__operator) {
case TypeOperator::RECORD:
{
list<string> fieldsList;
for(auto opIt = ++expr.operands.begin(); opIt!=expr.operands.end(); ++opIt){
fieldsList.push_back(getIndexStr(*opIt));
}
return controlIR.compileStructIndex(aggrRaw, aggrT, fieldsList);
};
case TypeOperator::ARRAY:
{
std::vector<llvm::Value*> indexes;
std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()),
[this] (const Expression & op) {
return process(op);
}
);
std::unique_ptr<containers::IContainersIR> containersIR(
containers::IContainersIR::create(aggrE, expectedT, ctx)
);
containers::ArrayIR* arraysIR = static_cast<containers::ArrayIR*>(containersIR.get());
return arraysIR->get(aggrRaw, indexes, hintAlias);
};
default:
assert(false);
}
};
case Operator::CALL_INTRINSIC:
{
// const std::string op = expr.getValueString();
//
// if (op == "copy") {
// llvm::Value* result = process(expr.getOperands().at(0));
//
// auto decoratorVersions = Decorators<VersionsScopeDecoratorTag>::getInterface(this);
// llvm::Value* storage = decoratorVersions->processIntrinsicInit(result->getType());
// decoratorVersions->processIntrinsicCopy(result, storage);
//
// return l.irBuilder.CreateLoad(storage, hintAlias);
// }
assert(false && "undefined intrinsic");
}
case Operator::QUERY:
case Operator::QUERY_LATE:
{
assert(false && "Should be processed by interpretation");
}
case Operator::VARIANT:
{
const ExpandedType& typResult = pass->man->root->getType(expr);
llvm::Type* typResultRaw = l.toLLVMType(typResult);
llvm::Type* typIdRaw = llvm::cast<llvm::StructType>(typResultRaw)->getElementType(0);
uint64_t id = expr.getValueDouble();
llvm::Value* resultRaw = llvm::UndefValue::get(typResultRaw);
resultRaw = l.irBuilder.CreateInsertValue(resultRaw, llvm::ConstantInt::get(typIdRaw, id), llvm::ArrayRef<unsigned>({0}));
const ExpandedType& typVariant = ExpandedType(typResult->__operands.at(id));
llvm::Type* typVariantRaw = l.toLLVMType(typVariant);
llvm::Value* variantRaw = llvm::UndefValue::get(typVariantRaw);
assert(expr.operands.size() == typVariant->__operands.size() && "Wrong variant arguments count");
if (!typVariant->__operands.size()) return resultRaw;
for (unsigned int fieldId = 0; fieldId < expr.operands.size(); ++fieldId) {
const ExpandedType& typField = ExpandedType(typVariant->__operands.at(fieldId));
Attachments::put<TypeInferred>(expr.operands.at(fieldId), typField);
llvm::Value* fieldRaw = process(expr.operands.at(fieldId));
assert(fieldRaw);
variantRaw = l.irBuilder.CreateInsertValue(variantRaw, fieldRaw, llvm::ArrayRef<unsigned>({fieldId}));
}
llvm::Type* typStorageRaw = llvm::cast<llvm::StructType>(typResultRaw)->getElementType(1);
llvm::Value* addrAsStorage = l.irBuilder.CreateAlloca(typStorageRaw);
llvm::Value* addrAsVariant = l.irBuilder.CreateBitOrPointerCast(addrAsStorage, typVariantRaw->getPointerTo());
l.irBuilder.CreateStore(variantRaw, addrAsVariant);
llvm::Value* storageRaw = l.irBuilder.CreateLoad(typStorageRaw, addrAsStorage);
resultRaw = l.irBuilder.CreateInsertValue(resultRaw, storageRaw, llvm::ArrayRef<unsigned>({1}));
return resultRaw;
}
case Operator::SWITCH_VARIANT:
{
- return controlIR.compileSwitchVariant(expr, DEFAULT("tmpswitch"));
+ return controlIR.compileSwitchVariant(expr, hintAlias);
}
case Operator::SWITCH_LATE:
{
assert(false && "Instruction's compilation should've been redirected to interpretation");
return nullptr;
}
case Operator::SEQUENCE:
{
return controlIR.compileSequence(expr);
}
case Operator::UNDEF:
{
llvm::Type* typExprUndef = l.toLLVMType(pass->man->root->getType(expr, expectedT));
return llvm::UndefValue::get(typExprUndef);
}
case Operator::UPDATE:
{
TypesHelper helper(pass->man->llvm);
containers::RecordIR irRecords(ctx);
const Expression& aggrE = expr.operands.at(0);
const Expression& updE = expr.operands.at(1);
const ExpandedType& aggrT = pass->man->root->getType(aggrE);
llvm::Value* aggrRaw = process(aggrE);
if (helper.isRecordT(aggrT)){
return irRecords.update(aggrRaw, aggrT, updE);
}
if (helper.isArrayT(aggrT)){
if (updE.op == Operator::LIST_INDEX){
std::unique_ptr<containers::IContainersIR> containersIR(
containers::IContainersIR::create(aggrE, TypeAnnotation(), ctx
));
return containersIR->update(aggrRaw, updE, hintAlias);
}
}
assert(false);
return nullptr;
}
case Operator::INVALID:
assert(expr.__state != Expression::COMPOUND);
switch (expr.__state) {
case Expression::IDENT:
{
Symbol s = Attachments::get<IdentifierSymbol>(expr);
return processSymbol(s, expr.getValueString());
}
case Expression::NUMBER:
{
llvm::Type* typConst = l.toLLVMType(pass->man->root->getType(expr, expectedT));
int literal = expr.getValueDouble();
if (typConst->isFloatingPointTy()) return llvm::ConstantFP::get(typConst, literal);
if (typConst->isIntegerTy()) return llvm::ConstantInt::get(typConst, literal);
assert(false && "Can't compile literal");
}
case Expression::STRING:
{
- return controlIR.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str"));
+ return controlIR.compileConstantStringAsPChar(expr.getValueString(), hintAlias);
};
default:
{
break;
}
};
break;
default: break;
}
assert(false && "Can't compile expression");
return 0;
}
llvm::Value*
BasicBruteScope::compile(const std::string& hintBlockDecl) {
LLVMLayer* llvm = pass->man->llvm;
if (!hintBlockDecl.empty()) {
llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm->llvmContext, hintBlockDecl, function->raw);
pass->man->llvm->irBuilder.SetInsertPoint(block);
}
lastBlockRaw = pass->man->llvm->irBuilder.GetInsertBlock();
Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope};
- return processSymbol(symbScope);
+
+ //set hint for an entry scope
+ string retAlias = (scope->__parent)? "" : function->prepareName();
+ return processSymbol(symbScope, retAlias);
}
IBruteScope::~IBruteScope() { }
IBruteFunction::~IBruteFunction() { }
llvm::Function*
IBruteFunction::compile() {
if (raw != nullptr) return raw;
LLVMLayer* llvm = pass->man->llvm;
llvm::IRBuilder<>& builder = llvm->irBuilder;
string&& functionName = prepareName();
std::vector<llvm::Type*>&& types = prepareSignature();
llvm::Type* expectedResultType = prepareResult();
llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false);
raw = llvm::cast<llvm::Function>(llvm->module->getOrInsertFunction(functionName, ft));
prepareBindings();
applyAttributes();
const std::string& blockName = "entry";
llvm::BasicBlock* blockCurrent = builder.GetInsertBlock();
llvm::Value* result = getBruteScope(__entry)->compile(blockName);
assert(result);
//SECTIONTAG types/convert function ret value
builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->irBuilder));
if (blockCurrent) {
builder.SetInsertPoint(blockCurrent);
}
llvm->moveToGarbage(ft);
return raw;
}
IBruteScope*
IBruteFunction::getBruteScope(const CodeScope * const scope) {
if (__scopes.count(scope)) {
auto result = __scopes.at(scope).lock();
if (result) {
return result.get();
}
}
std::shared_ptr<IBruteScope> unit(pass->buildCodeScopeUnit(scope, this));
if (scope->__parent != nullptr) {
auto parentUnit = Decorators<CachedScopeDecoratorTag>::getInterface(getBruteScope(scope->__parent));
parentUnit->registerChildScope(unit);
} else {
__orphanedScopes.push_back(unit);
}
if (!__scopes.emplace(scope, unit).second) {
__scopes[scope] = unit;
}
return unit.get();
}
IBruteScope*
IBruteFunction::getScopeUnit(ManagedScpPtr scope) {
return getBruteScope(&*scope);
}
IBruteScope*
IBruteFunction::getEntry() {
return getBruteScope(__entry);
}
std::vector<llvm::Type*>
IBruteFunction::getScopeSignature(CodeScope* scope){
LLVMLayer* llvm = IBruteFunction::pass->man->llvm;
AST* ast = IBruteFunction::pass->man->root;
std::vector<llvm::Type*> result;
std::transform(scope->__bindings.begin(), scope->__bindings.end(), std::inserter(result, result.end()),
- [llvm, ast, scope](const std::string & argAlias)->llvm::Type* {
+ [llvm, ast, scope](const std::string & argAlias)->llvm::Type* {
assert(scope->__identifiers.count(argAlias));
ScopedSymbol argS{scope->__identifiers.at(argAlias), versions::VERSION_NONE};
const Expression& argE = scope->__declarations.at(argS);
const ExpandedType& argT = ast->expandType(argE.type);
return llvm->toLLVMType(argT, argE);
});
+ if(scope->trackExternalSymbs){
+ std::transform(scope->boundExternalSymbs.begin(), scope->boundExternalSymbs.end(), std::inserter(result, result.end()),
+ [llvm, ast](const Symbol& argS){
+ const Expression& argE = CodeScope::getDefinition(argS);
+ const ExpandedType& argT = ast->expandType(argE.type);
+
+ return llvm->toLLVMType(argT, argE);
+ });
+ }
+
return result;
}
template<>
compilation::IBruteFunction*
CompilePassCustomDecorators<void, void>
::buildFunctionUnit(const ManagedFnPtr& function) {
return new BruteFunctionDefault(function, this);
}
template<>
compilation::IBruteScope*
CompilePassCustomDecorators<void, void>
::buildCodeScopeUnit(const CodeScope * const scope, IBruteFunction* function) {
return new DefaultCodeScopeUnit(scope, function, this);
}
std::string
BasicBruteScope::getIndexStr(const Expression& index){
switch(index.__state){
//named struct field
case Expression::STRING:
return index.getValueString();
break;
//anonymous struct field
case Expression::NUMBER:
return to_string((int) index.getValueDouble());
break;
default:
assert(false && "Wrong index for a struct");
}
return "";
}
} // end of compilation
compilation::IBruteFunction*
CompilePass::getBruteFn(const ManagedFnPtr& function) {
unsigned int id = function.id();
if (!functions.count(id)) {
compilation::IBruteFunction* unit = buildFunctionUnit(function);
functions.emplace(id, unit);
return unit;
}
return functions.at(id);
}
void
CompilePass::prepare(){
//Initialization:
#ifndef XREATE_CONFIG_MIN
#endif
managerTransformations = new xreate::compilation::TransformationsManager();
targetInterpretation = new interpretation::TargetInterpretation(man, this);
}
void
CompilePass::run() {
prepare();
//Determine entry function:
StaticModel modelEntry = man->transcend->query(analysis::FN_ENTRY_PREDICATE);
if (man->options.requireEntryFn){
assert(modelEntry.size() && "Error: No entry function found");
assert(modelEntry.size() == 1 && "Error: Ambiguous entry function");
}
if(modelEntry.size()){
string fnEntryName = std::get<0>(TranscendLayer::parse<std::string>(modelEntry.begin()->second));
compilation::IBruteFunction* fnEntry = getBruteFn(man->root->findFunction(fnEntryName));
__fnEntryRaw = fnEntry->compile();
}
//Compile exterior functions:
StaticModel modelExterior = man->transcend->query(analysis::FN_EXTERIOR_PREDICATE);
for(const auto entry: modelExterior){
const string& fnName = std::get<0>(TranscendLayer::parse<std::string>(entry.second));
getBruteFn(man->root->findFunction(fnName))->compile();
}
}
llvm::Function*
CompilePass::getEntryFunction() {
return __fnEntryRaw;
}
void
CompilePass::prepareQueries(TranscendLayer* transcend) {
#ifndef XREATE_CONFIG_MIN
transcend->registerQuery(new latex::LatexQuery(), QueryId::LatexQuery);
#endif
transcend->registerQuery(new containers::Query(), QueryId::ContainersQuery);
transcend->registerQuery(new demand::DemandQuery(), QueryId::DemandQuery);
transcend->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery);
}
} //end of namespace xreate
/**
* \class xreate::CompilePass
* \brief The owner of the compilation process. Performs fundamental compilation activities along with the xreate::compilation's routines
*
* xreate::CompilePass traverses over xreate::AST tree and produces executable code.
* The pass performs compilation using the following data sources:
* - %Attachments: the data gathered by the previous passes. See \ref xreate::Attachments.
* - Transcend solutions accessible via queries. See \ref xreate::IQuery, \ref xreate::TranscendLayer.
*
* The pass generates a bytecode by employing \ref xreate::LLVMLayer(wrapper over LLVM toolchain).
* Many compilation activities are delegated to more specific routines. Most notable delegated compilation aspects are:
* - Containers support. See \ref xreate::containers.
* - Latex compilation. See \ref xreate::latex.
* - Interpretation support. See \ref xreate::interpretation.
* - Loop saturation support. See \ref xreate::compilation::TransformationsScopeDecorator.
* - External code interaction support. See \ref xreate::ExternLayer (wrapper over Clang library).
*
* \section adaptability_sect Adaptability
* xreate::CompilePass's behaviour can be adapted in several ways:
* - %Function Decorators to alter function-level compilation. See \ref xreate::compilation::IBruteFunction
* - Code Block Decorators to alter code block level compilation. See \ref xreate::compilation::ICodeScopeUnit.
* Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit
* - Targets to allow more versitile extensions.
* Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See \ref xreate::compilation::Target.
* - Altering %function invocation. See \ref xreate::compilation::IFnInvocation.
*
* Clients are free to construct a compiler instantiation with the desired decorators by using \ref xreate::compilation::CompilePassCustomDecorators.
* As a handy alias, `CompilePassCustomDecorators<void, void>` constructs the default compiler.
*
- */
+ */
\ No newline at end of file
diff --git a/cpp/src/pass/compilepass.h b/cpp/src/pass/compilepass.h
index e7f2238..d050a82 100644
--- a/cpp/src/pass/compilepass.h
+++ b/cpp/src/pass/compilepass.h
@@ -1,233 +1,236 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: pgess <v.melnychenko@xreate.org>
*
* compilepass.h
*/
#ifndef COMPILEPASS_H
#define COMPILEPASS_H
#include "abstractpass.h"
#include "llvm/IR/Function.h"
namespace xreate {
class TranscendLayer;
class CompilePass;
class LLVMLayer;
namespace interpretation{
class TargetInterpretation;
}
}
namespace xreate { namespace compilation {
class IBruteScope;
class IBruteFunction;
class TransformationsManager;
/** \brief Holds current position in %AST while traversing*/
struct Context{
IBruteScope* scope;
IBruteFunction* function;
CompilePass* pass;
};
/** \brief Interface for custom function invocation operation compilation
* \details Default implementation is xreate::compilation::BruteFnInvocation
*/
class IFnInvocation {
public:
/** \brief Returns result of custom function invocation for the given arguments*/
virtual llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl="") = 0;
};
/** \brief Default IFnInvocation implementation */
class BruteFnInvocation: public IFnInvocation{
public:
BruteFnInvocation(llvm::Function* callee, LLVMLayer* l)
: __callee(callee), __calleeTy(callee->getFunctionType()), llvm(l) {}
BruteFnInvocation(llvm::Value* callee, llvm::FunctionType* ty, LLVMLayer* l)
: __callee(callee), __calleeTy(ty), llvm(l) {}
/** \brief Makes type conversions and returns LLVM call statement with given arguments*/
llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl="");
protected:
llvm::Value* __callee;
llvm::FunctionType* __calleeTy;
LLVMLayer* llvm;
};
/** \brief %Function invocation operator decorator to handle latex enabled functions with hidden extra arguments */
class HiddenArgsFnInvocation : public compilation::IFnInvocation{
public:
HiddenArgsFnInvocation(std::vector<llvm::Value *> args, compilation::IFnInvocation *parent)
: __args(args), __parent(parent){}
llvm::Value *operator()(std::vector<llvm::Value *> &&args, const std::string &hintDecl = "");
private:
std::vector<llvm::Value *> __args;
compilation::IFnInvocation *__parent;
};
/** \brief Interface to allow modification of CodeScope compilation
* \details Default implementation defined in xreate::compilation::DefaultCodeScopeUnit
*/
class IBruteScope{
public:
CompilePass* const pass;
IBruteFunction* const function;
const CodeScope* const scope;
llvm::BasicBlock* lastBlockRaw;
IBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass);
virtual ~IBruteScope();
virtual llvm::Value* compile(const std::string& hintBlockDecl="")=0;
virtual llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="")=0;
virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation())=0;
virtual Symbol bindArg(llvm::Value* value, std::string&& alias)=0;
virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0;
+ virtual void bindExternalSymb(llvm::Value*, const Symbol& s) = 0;
virtual void reset() = 0;
protected:
/** \brief For subclasses to implement this method to define a function name resolution*/
virtual IFnInvocation* findFunction(const Expression& opCall)=0;
};
/** \brief Minimal useful IBruteScope implementation suited for inheritance */
class BasicBruteScope: public IBruteScope{
public:
BasicBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass);
llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="") override;
llvm::Value* process(const Expression& expr, const std::string& hintAlias="", const TypeAnnotation& expectedT = TypeAnnotation()) override;
llvm::Value* compile(const std::string& hintBlockDecl="") override;
protected:
IFnInvocation* findFunction(const Expression& opCall) override;
private:
std::string getIndexStr(const Expression& index);
};
/** \brief Interface to specify compilation of %Function */
class IBruteFunction{
public:
- IBruteFunction(CodeScope* entry, CompilePass* p): pass(p), __entry(entry){}
- virtual ~IBruteFunction();
+ IBruteFunction(CodeScope* entry, CompilePass* p):
+ isLambda(false), pass(p), __entry(entry){}
+ virtual ~IBruteFunction();
- llvm::Function* compile();
+ llvm::Function* compile();
+ IBruteScope* getEntry();
+ virtual ManagedFnPtr getASTFn() const {return ManagedFnPtr();};
+ IBruteScope* getBruteScope(const CodeScope * const scope);
+ IBruteScope* getScopeUnit(ManagedScpPtr scope);
+ virtual std::string prepareName() = 0;
- IBruteScope* getEntry();
- virtual ManagedFnPtr getASTFn() const {return ManagedFnPtr();};
- IBruteScope* getBruteScope(const CodeScope * const scope);
- IBruteScope* getScopeUnit(ManagedScpPtr scope);
+ llvm::Function* raw = nullptr;
+ bool isLambda;
- llvm::Function* raw = nullptr;
-
protected:
- CompilePass* pass=nullptr;
- CodeScope* __entry;
-
- virtual std::string prepareName() = 0;
- virtual std::vector<llvm::Type*> prepareSignature() = 0;
- virtual llvm::Function::arg_iterator prepareBindings() = 0;
- virtual llvm::Type* prepareResult() = 0;
- virtual void applyAttributes() = 0;
+ CompilePass* pass=nullptr;
+ CodeScope* __entry;
+
+ virtual std::vector<llvm::Type*> prepareSignature() = 0;
+ virtual llvm::Function::arg_iterator prepareBindings() = 0;
+ virtual llvm::Type* prepareResult() = 0;
+ virtual void applyAttributes() = 0;
private:
- std::map<const CodeScope * const, std::weak_ptr<IBruteScope>> __scopes;
- std::list<std::shared_ptr<IBruteScope>> __orphanedScopes;
+ std::map<const CodeScope * const, std::weak_ptr<IBruteScope>> __scopes;
+ std::list<std::shared_ptr<IBruteScope>> __orphanedScopes;
protected:
std::vector<llvm::Type*> getScopeSignature(CodeScope* scope);
};
/** \brief Minimal useful IBruteFunction implementation suited for inheritance */
class BasicBruteFunction: public IBruteFunction{
public:
- BasicBruteFunction(ManagedFnPtr f, CompilePass* p)
+ BasicBruteFunction(ManagedFnPtr f, CompilePass* p)
: IBruteFunction(f->getEntryScope(), p), __function(f) {}
+ std::string prepareName() override;
+
protected:
- std::string prepareName() override;
virtual std::vector<llvm::Type*> prepareSignature() override;
virtual llvm::Type* prepareResult() override;
virtual llvm::Function::arg_iterator prepareBindings() override;
virtual void applyAttributes() override;
virtual ManagedFnPtr getASTFn() const {return __function;};
protected:
ManagedFnPtr __function;
};
} // end of namespace compilation
class CompilePass : public AbstractPass<void> {
friend class compilation::BasicBruteScope;
friend class compilation::IBruteFunction;
public:
compilation::TransformationsManager* managerTransformations;
interpretation::TargetInterpretation* targetInterpretation;
CompilePass(PassManager* manager): AbstractPass<void>(manager) {}
/** \brief Executes compilation process */
void run() override;
/**\brief Returns compiled specified %Function
* \details Executes function compilation or read cache if it's already done
*/
compilation::IBruteFunction* getBruteFn(const ManagedFnPtr& function);
/**\brief Returns compiled main(entry) %Function in program */
llvm::Function* getEntryFunction();
/** \brief Initializes queries required by compiler. See xreate::IQuery, xreate::TranscendLayer */
static void prepareQueries(TranscendLayer* transcend);
void prepare();
protected:
virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function)=0;
virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, compilation::IBruteFunction* function)=0;
private:
//TODO free `functions` in destructor
std::map<unsigned int, compilation::IBruteFunction*> functions;
llvm::Function* __fnEntryRaw = 0;
};
namespace compilation{
/** \brief Constructs compiler with desired %Function and %Code Scope decorators. See adaptability in xreate::CompilePass*/
template<class FUNCTION_DECORATOR=void, class SCOPE_DECORATOR=void>
class CompilePassCustomDecorators: public ::xreate::CompilePass{
public:
CompilePassCustomDecorators(PassManager* manager): ::xreate::CompilePass(manager) {}
virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function) override{
return new FUNCTION_DECORATOR(function, this);
}
virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function) override{
return new SCOPE_DECORATOR(scope, function, this);
}
};
template<>
compilation::IBruteFunction*
CompilePassCustomDecorators<void, void>::buildFunctionUnit(const ManagedFnPtr& function);
template<>
compilation::IBruteScope*
CompilePassCustomDecorators<void, void>::buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function);
}} //end of namespace xreate::compilation
#endif // COMPILEPASS_H
diff --git a/cpp/src/pass/interpretationpass.cpp b/cpp/src/pass/interpretationpass.cpp
index 5f1d29e..5a7b9bb 100644
--- a/cpp/src/pass/interpretationpass.cpp
+++ b/cpp/src/pass/interpretationpass.cpp
@@ -1,563 +1,563 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: interpretationpass.cpp
* Author: pgess <v.melnychenko@xreate.org>
*
* Created on July 5, 2016, 5:21 PM
*/
/**
* \file interpretationpass.h
* \brief Interpretation analysis: determines what parts of a code could be interpreted
*/
#include "ast.h"
#include "pass/interpretationpass.h"
#include "compilation/targetinterpretation.h"
#include "analysis/utils.h"
#include "analysis/predefinedanns.h"
#include <bits/stl_vector.h>
//DEBT implement InterpretationPass purely in transcend
//DEBT represent InterpretationPass as general type inference
using namespace std;
namespace xreate {
template<>
interpretation::InterpretationResolution
defaultValue<interpretation::InterpretationResolution>() {
return interpretation::CMPL_ONLY;
}
namespace interpretation {
enum InterpretationQuery {
QUERY_INTR_ONLY, QUERY_CMPL_ONLY
};
namespace details {
template<InterpretationQuery FLAG_REQUIRED>
bool
checkConstraints(InterpretationResolution flag) {
return( (flag==INTR_ONLY&&FLAG_REQUIRED==QUERY_INTR_ONLY)
||(flag==CMPL_ONLY&&FLAG_REQUIRED==QUERY_CMPL_ONLY));
}
InterpretationResolution
recognizeTags(const map<std::string, Expression>& tags) {
std::list<Expression> tagsL;
auto predefined = analysis::PredefinedAnns::instance();
for(const auto& tag: tags){tagsL.push_back(tag.second);}
const Expression& tagI12nE = analysis::findAnnById(
(unsigned) analysis::PredefinedAnns::ExprAnnotations::I12N,
ExpandedType(predefined.exprAnnsT),
tagsL);
if (!tagI12nE.isValid()) return ANY;
analysis::PredefinedAnns::I12ModeTag modeI12n = (analysis::PredefinedAnns::I12ModeTag) tagI12nE.operands.at(0).getValueDouble();
switch(modeI12n){
case analysis::PredefinedAnns::I12ModeTag::ON:
return INTR_ONLY;
case analysis::PredefinedAnns::I12ModeTag::OFF:
return CMPL_ONLY;
}
return ANY;
}
}
InterpretationResolution
unify(InterpretationResolution flag) {
return flag;
}
template<typename FLAG_A, typename FLAG_B, typename... FLAGS>
InterpretationResolution
unify(FLAG_A flagA, FLAG_B flagB, FLAGS... flags) {
if(flagA==ANY){
return unify(flagB, flags...);
}
if(flagB==ANY){
return unify(flagA, flags...);
}
assert(flagA==flagB);
return flagA;
}
template<InterpretationQuery FLAG_REQUIRED>
bool
checkConstraints(std::vector<InterpretationResolution>&& flags) {
assert(flags.size());
InterpretationResolution flag=flags.front();
return details::checkConstraints<FLAG_REQUIRED>(flag);
}
template<InterpretationQuery FLAG_REQUIRED_A, InterpretationQuery FLAG_REQUIRED_B, InterpretationQuery... FLAGS>
bool
checkConstraints(std::vector<InterpretationResolution>&& flags) {
assert(flags.size());
InterpretationResolution flag=flags.front();
flags.pop_back();
if(details::checkConstraints<FLAG_REQUIRED_A>(flag)){
return checkConstraints<FLAG_REQUIRED_B, FLAGS...>(move(flags));
}
return false;
}
bool
InterpretationData::isDefault() const {
return(resolution==ANY&&op==NONE);
}
void
recognizeTags(const Expression& e) {
InterpretationData tag{details::recognizeTags(e.tags), NONE};
if(!tag.isDefault())
Attachments::put<InterpretationData>(e, tag);
}
InterpretationResolution
recognizeTags(const ManagedFnPtr& f) {
return details::recognizeTags(f->getTags());
}
InterpretationPass::InterpretationPass(PassManager* manager)
: AbstractPass(manager) {
Attachments::init<I12nFunctionSpec>();
Attachments::init<InterpretationData>();
}
void
InterpretationPass::run() {
ManagedFnPtr f=man->root->begin<Function>();
auto& visitedSymbols=getSymbolCache();
while(f.isValid()) {
const Symbol&symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()};
if(!visitedSymbols.isCached(symbolFunction)){
visitedSymbols.setCachedValue(symbolFunction, process(f));
}
++f;
}
}
InterpretationResolution
InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl) {
recognizeTags(expression);
InterpretationResolution resolution=ANY;
InterpretationOperator opNo=NONE;
switch(expression.__state) {
case Expression::NUMBER:
case Expression::STRING:
{
break;
}
case Expression::IDENT:
{
resolution=Parent::processSymbol(Attachments::get<IdentifierSymbol>(expression), context);
break;
}
case Expression::COMPOUND:
break;
default:
{
resolution=CMPL_ONLY;
break;
}
}
if(expression.__state==Expression::COMPOUND)
switch(expression.op) {
case Operator::EQU:
case Operator::NE:
{
InterpretationResolution left=process(expression.operands[0], context);
InterpretationResolution right=process(expression.operands[1], context);
resolution=unify(left, right);
break;
}
case Operator::LOGIC_AND:
{
assert(expression.operands.size()==1);
resolution=process(expression.operands[0], context);
break;
}
case Operator::CALL:
{
size_t sizeOperands = expression.operands.size();
std::vector<InterpretationResolution> operands;
operands.reserve(sizeOperands);
for(size_t opNo=0; opNo<sizeOperands; ++opNo) {
const Expression &operand=expression.operands[opNo];
operands.push_back(process(operand, context));
}
//TODO cope with static/dynamic context
//TODO BUG here: if several variants they all are processed as CMPL regardless of signature
list<ManagedFnPtr> callees=man->root->getFnSpecializations(expression.getValueString());
if(callees.size()!=1){
resolution=CMPL_ONLY;
break;
}
ManagedFnPtr callee=callees.front();
const Symbol& symbCalleeFunc{ScopedSymbol::RetSymbol, callee->getEntryScope()};
//recursion-aware processing:
// - skip self recursion
const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, context.function->getEntryScope()};
if(!(symbSelfFunc==symbCalleeFunc)){
InterpretationResolution resCallee=processFnCall(callee, context);
assert(resCallee!=FUNC_POSTPONED&&"Indirect recursion detected: can't decide on interpretation resolution");
resolution=unify(resolution, resCallee);
}
//check arguments compatibility
const I12nFunctionSpec& calleeSignature=FunctionInterpretationHelper::getSignature(callee);
for(size_t opNo=0; opNo<sizeOperands; ++opNo){
InterpretationResolution argActual=operands.at(opNo);
InterpretationResolution argExpected=calleeSignature.signature[opNo];
//TODO use args unification result to properly process function call
unify(argActual, argExpected);
}
if(FunctionInterpretationHelper::needPartialInterpretation(callee)){
opNo=CALL_INTERPRET_PARTIAL;
}
break;
}
case Operator::CALL_INTRINSIC:
{
switch((IntrinsicFn) expression.getValueDouble()){
case IntrinsicFn::REC_FIELDS: resolution = INTR_ONLY; break;
default: resolution=CMPL_ONLY; break;
}
break;
}
case Operator::QUERY:
{
resolution=INTR_ONLY;
break;
}
case Operator::QUERY_LATE:
{
InterpretationResolution predicate=process(expression.operands[0], context);
unify(predicate, INTR_ONLY);
CodeScope* exprBody=expression.blocks.front();
const std::string& argName=expression.bindings[0];
Symbol argS = {
ScopedSymbol{exprBody->__identifiers.at(argName), versions::VERSION_NONE},
exprBody
};
getSymbolCache().setCachedValue(argS, INTR_ONLY);
Parent::process(expression.blocks.front(), context);
resolution = CMPL_ONLY;
opNo=QUERY_LATE;
break;
}
case Operator::SWITCH_LATE:
{
resolution = CMPL_ONLY;
opNo = SWITCH_LATE;
break;
}
case Operator::IF:
{
InterpretationResolution flagCondition=process(expression.getOperands()[0], context);
InterpretationResolution flagScope1=Parent::process(expression.blocks.front(), context);
InterpretationResolution flagScope2=Parent::process(expression.blocks.back(), context);
//special case: IF_INTERPRET_CONDITION
if(checkConstraints<QUERY_INTR_ONLY>({flagCondition})){
opNo=IF_INTERPRET_CONDITION;
flagCondition=ANY;
}
resolution=unify(flagCondition, flagScope1, flagScope2);
break;
}
case Operator::FOLD:
{
InterpretationResolution flagInput=process(expression.getOperands()[0], context);
InterpretationResolution flagAccumInit=process(expression.getOperands()[1], context);
CodeScope* scopeBody=expression.blocks.front();
const std::string& nameEl=expression.bindings[0];
Symbol symbEl{ScopedSymbol
{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody};
getSymbolCache().setCachedValue(symbEl, InterpretationResolution(flagInput));
const std::string& nameAccum=expression.bindings[1];
Symbol symbAccum{ScopedSymbol
{scopeBody->__identifiers.at(nameAccum), versions::VERSION_NONE}, scopeBody};
getSymbolCache().setCachedValue(symbAccum, InterpretationResolution(flagAccumInit));
InterpretationResolution flagBody=Parent::process(expression.blocks.front(), context);
//special case: FOLD_INTERPRET_INPUT
if(checkConstraints<QUERY_INTR_ONLY>({flagInput})){
opNo=FOLD_INTERPRET_INPUT;
flagInput=ANY;
}
resolution=unify(flagInput, flagAccumInit, flagBody);
break;
}
case Operator::INDEX:
{
for(const Expression &opNo : expression.getOperands()) {
resolution=unify(resolution, process(opNo, context));
}
break;
}
case Operator::SWITCH:
{
InterpretationResolution flagCondition=process(expression.operands[0], context);
bool hasDefaultCase=expression.operands[1].op==Operator::CASE_DEFAULT;
//determine conditions resolution
InterpretationResolution flagHeaders=flagCondition;
for(size_t size=expression.operands.size(), i=hasDefaultCase?2:1; i<size; ++i) {
const Expression& exprCase=expression.operands[i];
flagHeaders=unify(flagHeaders, Parent::process(exprCase.blocks.front(), context));
}
if(checkConstraints<QUERY_INTR_ONLY>({flagHeaders})){
opNo=SWITCH_INTERPRET_CONDITION;
flagHeaders=ANY;
}
//determine body resolutions
resolution=flagHeaders;
for(size_t size=expression.operands.size(), i=1; i<size; ++i) {
const Expression& exprCase=expression.operands[i];
resolution=unify(resolution, Parent::process(exprCase.blocks.back(), context));
}
break;
}
case Operator::SWITCH_VARIANT:
{
InterpretationResolution resolutionCondition=process(expression.operands.at(0), context);
resolution=resolutionCondition;
if(checkConstraints<QUERY_INTR_ONLY>({resolution})){
opNo=SWITCH_VARIANT;
resolution=ANY;
}
const string identCondition=expression.bindings.front();
for(auto scope : expression.blocks) {
//set binding resolution
- ScopedSymbol symbolInternal=scope->getSymbol(identCondition);
+ ScopedSymbol symbolInternal= scope->findSymbolByAlias(identCondition);
getSymbolCache().setCachedValue(Symbol{symbolInternal, scope}, InterpretationResolution(resolutionCondition));
resolution=unify(resolution, Parent::process(scope, context));
}
for(auto scope : expression.blocks) {
resolution=unify(resolution, Parent::process(scope, context));
}
break;
}
case Operator::LIST:
{
for(const Expression &opNo : expression.getOperands()) {
resolution=unify(resolution, process(opNo, context));
}
break;
}
case Operator::VARIANT:
{
if(expression.getOperands().size()){
resolution=process(expression.getOperands().front(), context);
} else {
resolution=ANY;
}
break;
}
default:
{
resolution=CMPL_ONLY;
for(const Expression &opNo : expression.getOperands()) {
process(opNo, context);
}
for(CodeScope* scope : expression.blocks) {
Parent::process(scope, context);
}
break;
}
}
InterpretationData dataExpected=
Attachments::get<InterpretationData>(expression,{ANY, NONE});
resolution=unify(resolution, dataExpected.resolution);
if(resolution!=dataExpected.resolution || opNo != dataExpected.op ){
Attachments::put<InterpretationData>(expression,{resolution, opNo});
}
return resolution;
}
InterpretationResolution
InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context) {
return process(function);
}
InterpretationResolution
InterpretationPass::process(ManagedFnPtr function) {
CodeScope* entry=function->getEntryScope();
std::vector<std::string> arguments=entry->__bindings;
const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, function->getEntryScope()};
auto& cache=getSymbolCache();
if(cache.isCached(symbSelfFunc))
return cache.getCachedValue(symbSelfFunc);
const I12nFunctionSpec& fnSignature=FunctionInterpretationHelper::getSignature(function);
InterpretationResolution fnResolutionExpected=details::recognizeTags(function->getTags());
//mark preliminary function resolution as expected
if(fnResolutionExpected!=ANY){
cache.setCachedValue(symbSelfFunc, move(fnResolutionExpected));
} else {
// - in order to recognize indirect recursion mark this function resolution as POSTPONED
cache.setCachedValue(symbSelfFunc, FUNC_POSTPONED);
}
//set resolution for function arguments as expected
for(int argNo=0, size=arguments.size(); argNo<size; ++argNo) {
Symbol symbArg{ScopedSymbol
{entry->__identifiers.at(arguments[argNo]), versions::VERSION_NONE}, entry};
cache.setCachedValue(symbArg, InterpretationResolution(fnSignature.signature[argNo]));
}
PassContext context;
context.function=function;
context.scope=entry;
InterpretationResolution resActual=process(CodeScope::getDefinition(symbSelfFunc), context);
resActual=unify(resActual, fnResolutionExpected);
return cache.setCachedValue(symbSelfFunc, move(resActual));
}
const I12nFunctionSpec
FunctionInterpretationHelper::getSignature(ManagedFnPtr function) {
if(Attachments::exists<I12nFunctionSpec>(function)){
return Attachments::get<I12nFunctionSpec>(function);
}
I12nFunctionSpec&& data=recognizeSignature(function);
Attachments::put<I12nFunctionSpec>(function, data);
return data;
}
I12nFunctionSpec
FunctionInterpretationHelper::recognizeSignature(ManagedFnPtr function) {
CodeScope* entry=function->__entry;
I12nFunctionSpec result;
result.signature.reserve(entry->__bindings.size());
bool flagPartialInterpretation=false;
for(size_t no=0, size=entry->__bindings.size(); no<size; ++no) {
const std::string& argName=entry->__bindings[no];
Symbol symbArg{ScopedSymbol
{entry->__identifiers.at(argName), versions::VERSION_NONE}, entry};
const Expression& arg=CodeScope::getDefinition(symbArg);
InterpretationResolution argResolution=details::recognizeTags(arg.tags);
flagPartialInterpretation|=(argResolution==INTR_ONLY);
result.signature.push_back(argResolution);
}
result.flagPartialInterpretation=flagPartialInterpretation;
return result;
}
bool
FunctionInterpretationHelper::needPartialInterpretation(ManagedFnPtr function) {
const I12nFunctionSpec& data=getSignature(function);
return data.flagPartialInterpretation;
}
}
} //end of namespace xreate::interpretation
/** \class xreate::interpretation::InterpretationPass
*
* The class encapsulates *Interpretation Analysis* to support [Interpretation](/d/concepts/interpretation/).
*
* It recognizes program functions, expressions, instructions eligible for interpretation
* and stores the output in \ref Attachments<I12nFunctionSpec> and \ref Attachments<InterpretationData>
*
* There are number of instructions currently eligible for interpretation:
* - Basic literals: numbers and strings
* - Compounds: lists, structs, variants
* - Non-versioned identifiers
* - Comparison and logic operators
* - %Function calls
* - `query` intrinsic function calls
* - Branching: `if`, `loop fold`, `switch`, `switch variant` statements
*
* Some of these instructions are eligible also for *late interpretation* to allow coupling
* of compiled instructions with interpreted ones, those are:
* - Partial function calls
* - Branching: `if`, `loop fold`, `switch`, `switch variant` statements
*
* \sa xreate::interpretation::TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/)
*/
diff --git a/cpp/tests/ast.cpp b/cpp/tests/ast.cpp
index 2f76ecf..4e97c6a 100644
--- a/cpp/tests/ast.cpp
+++ b/cpp/tests/ast.cpp
@@ -1,288 +1,307 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* ast.cpp
*
* Created on: Jun 11, 2015
* Author: pgess <v.melnychenko@xreate.org>
*/
#include "supplemental/docutils.h"
#include "xreatemanager.h"
#include "main/Parser.h"
#include "supplemental/basics.h"
#include "gtest/gtest.h"
using namespace std;
using namespace xreate;
using namespace xreate::grammar::main;
TEST(AST, Containers1) {
FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r");
Scanner scanner(input);
Parser parser(&scanner);
parser.Parse();
assert(!parser.errors->count && "Parser errors");
fclose(input);
}
TEST(AST, InterfacesDataCFA) {
XreateManager* man = XreateManager::prepare
("interface(cfa){\n"
" operator map :: annotation1.\n"
"}");
auto answer = man->root->__interfacesData.equal_range(CFA);
EXPECT_EQ(1, std::distance(answer.first, answer.second));
Expression&& scheme = move(answer.first->second);
EXPECT_EQ(Operator::MAP, scheme.op);
EXPECT_EQ("annotation1", scheme.getOperands().at(0).getValueString());
}
TEST(AST, syntax_recognizeIdentifiers) {
XreateManager* man = XreateManager::prepare(R"Code(
test= function(a:: num):: num; entry {
a = b:: int.
b = 8:: int.
a
}
)Code");
}
TEST(AST, syntax_operatorIndex) {
XreateManager* man = XreateManager::prepare(R"Code(
test= function(a:: num):: num; entry {
b = a[1].
b
}
)Code");
}
TEST(AST, IdentHyphen1){
XreateManager* man = XreateManager::prepare(R"Code(
my-fn = function(m-n:: num):: num; entry
{
b = m-n-1:: int.
b
}
)Code");
}
TEST(AST, Variants_switch) {
XreateManager* man = XreateManager::prepare(R"Code(
Color = type variant{Blue, White, Green}.
main = function:: int {
x = White()::Color.
switch variant(x)::int
case (Green) {0}
case (White) {1}
case (Blue){2}
}
)Code");
Expression e = man->root->findFunction("main")->getEntryScope()->getBody();
ASSERT_EQ(4, e.getOperands().size());
ASSERT_EQ(3, e.blocks.size());
}
TEST(AST, TypeVariantEmpty){
std::string code = R"(
my-rec-t = type variant{}
)";
ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 29: Variant type can't be empty.");
}
+TEST(AST, Lambda_BoundVars_1){
+ string code = R"(
+ myfn = function:: int {
+ a = [1..5]:: [int].
+ offset = 10:: int.
+ loop map(a->x:: int):: [int] { x + offset:: int}
+ }
+ )";
+
+ auto man = details::tier1::XreateManager::prepare(move(code));
+ CodeScope* scopeEntry = man->root->findFunction("myfn")->getEntryScope();
+ const string& offsetAlias = "offset";
+ ScopedSymbol offsetS = scopeEntry->findSymbolByAlias(offsetAlias);
+
+ CodeScope* scopeMap = scopeEntry->getBody().blocks.front();
+ ASSERT_EQ(1, scopeMap->boundExternalSymbs.size());
+ ASSERT_TRUE(scopeMap->boundExternalSymbs.count(Symbol{offsetS, scopeEntry}));
+}
+
TEST(AST, DISABLED_InterfacesDataDFA) { }
TEST(AST, DISABLED_InterfacesDataExtern) { }
TEST(AST, Doc_LiteralsAndExpressions) {
XreateManager* man = XreateManager::prepare(
R"Code(
Record1 = type {year:: int, month:: string}.
isOdd = function(x :: int) :: bool {true}
test = function:: bool; entry {
x1 = 5 :: int.
x2 = "Nimefurahi kukujua":: string.
x3 = {year = 1934, month = "april"}:: Record1.
x4 = {16, 8, 3} :: [int].
x41 = [1..18]:: [int].
x5 = 8>=3:: bool.
x6 = "Blue" <> "Green" :: bool.
x7 = -true:: bool.
colors = {"Green", "Blue"} :: [string].
color = colors[0] :: string.
date = {year = 1934, month = "april"}:: Record1. year = date["year"] :: int.
a = 0::int. b = 0 :: int.
x7 = a - b:: int.
result = isOdd(6) :: bool.
true
}
)Code");
ASSERT_TRUE(true);
}
TEST(AST, Doc_CodeBlocks1) {
XreateManager* man = XreateManager::prepare(
getDocumentationExampleById("documentation/Syntax/syntax.xml", "CodeBlocks1"));
FnNoArgs resultFn = (FnNoArgs) man->run();
int resultExpected = resultFn();
ASSERT_EQ(12, resultExpected);
}
TEST(AST, Doc_Functions1) {
XreateManager* man = XreateManager::prepare(
getDocumentationExampleById("documentation/Syntax/syntax.xml", "Functions1"));
ASSERT_TRUE(true);
}
TEST(AST, Doc_FunctionSpecializations1) {
XreateManager* man = XreateManager::prepare(
getDocumentationExampleById("documentation/Syntax/syntax.xml", "FunctionSpecialization1"));
ASSERT_TRUE(true);
}
TEST(AST, Doc_BranchStatements) {
string code_IfStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "IfStatement1");
string code_SwitchStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "SwitchStatement1");
string code =
R"Code(
test = function:: int; entry
{
question = "Favorite color?":: string.
monthNum = 2:: int.
%IfStatement1
%SwitchStatement1
monthName
}
)Code";
replace(code, "%IfStatement1", code_IfStatement1);
replace(code, "%SwitchStatement1", code_SwitchStatement1);
XreateManager* man = XreateManager::prepare(move(code));
ASSERT_TRUE(true);
}
TEST(AST, Doc_LoopStatements) {
string code_LoopStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement1");
string code_LoopStatement2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement2");
string code_FoldStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "FoldStatement1");
string code_MapStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "MapStatement1");
string code =
R"Code(
test = function:: int; entry
{
%LoopStatement1
%LoopStatement2
%FoldStatement1
%MapStatement1
min
}
)Code";
replace(code, "%LoopStatement1", code_LoopStatement1);
replace(code, "%LoopStatement2", code_LoopStatement2);
replace(code, "%FoldStatement1", code_FoldStatement1);
replace(code, "%MapStatement1", code_MapStatement1);
XreateManager::prepare(move(code));
ASSERT_TRUE(true);
}
TEST(AST, Doc_Types){
string code = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Types1");
XreateManager::prepare(move(code));
ASSERT_TRUE(true);
}
TEST(AST, Doc_Variants1){
string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Variants1");
XreateManager::prepare(move(code_Variants1));
ASSERT_TRUE(true);
}
TEST(AST, Doc_VariantsSwitch1){
string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "VariantsSwitch1");
XreateManager::prepare(move(code_Variants1));
ASSERT_TRUE(true);
}
TEST(AST, Doc_Versions1){
string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_1");
string code_Variants2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_2");
string code = R"Code(
test = function:: int; entry
{
<BODY>
y
})Code";
{
std::cout << code_Variants1 << std::endl;
XreateManager* man = XreateManager::prepare(move(code_Variants1));
man->run();
delete man;
ASSERT_TRUE(true);
}
// {
// replace(code, "<BODY>", code_Variants2);
// auto man = details::tier1::XreateManager::prepare(move(code));
// ASSERT_DEATH(man->analyse(), ".*versions graph.*");
// }
}
TEST(AST, Intrinsics1){
string code = R"Code(
test = function:: [int]
{
intrinsic array_init(8):: [int]
})Code";
XreateManager* man = XreateManager::prepare(move(code));
const Expression bodyE = man->root->findFunction("test")->getEntryScope()->getBody();
ASSERT_EQ(Operator::CALL_INTRINSIC, bodyE.op);
ASSERT_EQ(IntrinsicFn ::ARR_INIT, (IntrinsicFn) bodyE.getValueDouble());
}
TEST(AST, TypeRecordEmpty){
std::string code = R"(
my-rec-t = type {}
)";
ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 22: Record type can't be empty.");
}
TEST(AST, PredPredicates1){
string code = R"(
my-fn = function:: int; entry() {0}
)";
auto man = XreateManager::prepare(move(code));
}
\ No newline at end of file
diff --git a/cpp/tests/compilation.cpp b/cpp/tests/compilation.cpp
index e019829..46688e9 100644
--- a/cpp/tests/compilation.cpp
+++ b/cpp/tests/compilation.cpp
@@ -1,346 +1,378 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* compilation.cpp
*
* Created on: -
* Author: pgess <v.melnychenko@xreate.org>
*/
#include "xreatemanager.h"
#include "supplemental/basics.h"
#include "llvmlayer.h"
#include "pass/compilepass.h"
#include "compilation/lambdas.h"
#include "gtest/gtest.h"
using namespace xreate;
using namespace xreate::compilation;
using namespace std;
//DEBT implement no pkgconfig ways to link libs
//TOTEST FunctionUnit::compileInline
TEST(Compilation, functionEntry1){
std::unique_ptr<XreateManager> program(XreateManager::prepare(
"func1 = function(a:: int):: int {a+8} \
func2 = function::int; entry {12 + func1(4)} \
"));
void* entryPtr = program->run();
int (*entry)() = (int (*)())(intptr_t)entryPtr;
int answer = entry();
ASSERT_EQ(24, answer);
}
TEST(Compilation, full_IFStatementWithVariantType){
XreateManager* man = XreateManager::prepare(
"Color = type variant {RED, BLUE, GREEN}.\n"
"\n"
" main = function(x::int):: bool; entry {\n"
" color = if (x == 0 )::Color {RED()} else {BLUE()}.\n"
" if (color == BLUE())::bool {true} else {false}\n"
" }"
);
bool (*main)(int) = (bool (*)(int)) man->run();
ASSERT_FALSE(main(0));
ASSERT_TRUE(main(1));
}
TEST(Compilation, full_Variant1){
XreateManager* man = XreateManager::prepare(R"Code(
global = type predicate {
entry
}
Command= type variant{
Add(x::int, y::int),
Dec(x::int)
}.
main = function::Command; entry() {
Dec(2) ::Command
}
)Code");
void (*main)() = (void (*)()) man->run();
}
TEST(Compilation, full_SwitchVariant1){
XreateManager* man = XreateManager::prepare(R"Code(
Command= type variant{
Add(x::int, y::int),
Dec(x::int)
}.
main = function::int; entry {
command = Add(3, 5):: Command.
switch variant(command)::int
case(Add){command["x"] + command["y"]}
case(Dec){command["x"]}
}
)Code");
int (*mainFn)() = (int (*)()) man->run();
int result = mainFn();
ASSERT_EQ(8, result);
}
TEST(Compilation, full_SwitchVariantNoArguments2){
XreateManager* man = XreateManager::prepare(R"Code(
Command= type variant{Add, Dec}.
main = function::int; entry {
command = Dec():: Command.
switch variant(command)::int
case(Add){0}
case(Dec){1}
}
)Code");
int (*mainFn)() = (int (*)()) man->run();
int result = mainFn();
ASSERT_EQ(1, result);
}
TEST(Compilation, full_SwitchVariantMixedArguments3){
XreateManager* man = XreateManager::prepare(R"Code(
Command= type variant{
Add(x::int, y::int),
Dec
}.
main = function(arg::int):: int; entry {
command = if (arg > 0)::Command {Dec()} else {Add(1, 2)}.
switch variant(command)::int
case(Add){0}
case(Dec){1}
}
)Code");
int (*mainFn)(int) = (int (*)(int)) man->run();
int result = mainFn(5);
ASSERT_EQ(1, result);
}
TEST(Compilation, full_StructUpdate){
XreateManager* man = XreateManager::prepare(
R"Code(
Rec = type {
a :: int,
b:: int
}.
test= function:: int; entry {
a = {a = 18, b = 20}:: Rec.
b = a + {a = 11}:: Rec.
b["a"]
}
)Code");
int (*main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(11, result);
}
TEST(Compilation, AnonymousStruct_init_index){
std::string code =
R"Code(
main = function:: int; entry {
x = {10, 15} :: {int, int}.
x[1]
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
int (*main)() = (int (*)()) man->run();
EXPECT_EQ(15, main());
}
TEST(Compilation, AnonymousStruct_init_update){
std::string code =
R"Code(
main = function:: int; entry {
x = {10, 15} :: {int, int}.
y = x + {6}:: {int, int}.
y[0]
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
int (*main)() = (int (*)()) man->run();
EXPECT_EQ(6, main());
}
TEST(Compilation, BugIncorrectScopes1){
std::string code =
R"Code(
init = function:: int {10}
main = function(cmd:: int):: int; entry {
x = init():: int.
if(cmd > 0):: int { x + 1 } else { x }
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
int (*mainFn)(int) = (int (*)(int)) man->run();
EXPECT_EQ(11, mainFn(1));
}
TEST(Compilation, Sequence1){
std::string code =
R"Code(
interface(extern-c){
libbsd = library:: pkgconfig("libbsd").
include {
libbsd = {"bsd/stdlib.h", "string.h"}
}.
}
start = function:: i32; entry {
seq {
nameNew = "TestingSequence":: string.
setprogname(nameNew)
} {strlen(getprogname())}::i32
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
int (*startFn)() = (int (*)()) man->run();
int nameNewLen = startFn();
ASSERT_EQ(15, nameNewLen);
}
TEST(Compilation, BoolInstructions1){
std::string code =
R"Code(
test = function (a:: bool, b:: bool):: bool; entry
{
-a
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
Fn2Args startFn = (Fn2Args) man->run();
}
TEST(Compilation, StructIndex1){
std::string code =
R"Code(
Anns = type predicate {
entry()
}
test = function:: int; entry()
{
x = {a = ({b = 3}::{b:: int})}:: {a:: {b:: int}}.
2 + x["a", "b"] + x["a"]["b"]
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
FnNoArgs startFn = (FnNoArgs) man->run();
int result = startFn();
ASSERT_EQ(2, result);
}
TEST(Compilation, PreferredInt1){
std::unique_ptr<XreateManager> man(XreateManager::prepare(""));
TypesHelper utils(man->llvm);
int bitwidth = utils.getPreferredIntTy()->getBitWidth();
ASSERT_EQ(64, bitwidth);
}
TEST(Compilation, PredPredicates1){
string code = R"(
my-fn = function:: int; entry() {0}
)";
auto man = XreateManager::prepare(move(code));
FnNoArgs startFn = (FnNoArgs) man->run();
int result = startFn();
ASSERT_EQ(0, result);
}
typedef intmax_t (*FnI_I)(intmax_t);
TEST(Compilation, Lambda1){
string code = R"(
myfn = function:: int {
a = [1..5]:: [int].
loop map(a->x:: int):: [int] { x + 10:: int}
}
)";
auto man = details::tier1::XreateManager::prepare(move(code));
LLVMLayer* llvm = man->llvm;
man->analyse();
std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<>(man));
compiler->prepare();
LambdaIR compilerLambda(compiler.get());
CodeScope* scopeLoop = man->root->findFunction("myfn")->getEntryScope()->getBody().blocks.front();
auto fnRaw = compilerLambda.compile(scopeLoop, "loop");
llvm->initJit();
FnI_I fn = (FnI_I)llvm->getFunctionPointer(fnRaw);
ASSERT_EQ(20, fn(10));
}
+TEST(Compilation, Lambda_BoundVars1){
+ string code = R"(
+ myfn = function:: int {
+ a = [1..5]:: [int].
+ offset = 10:: int.
+ loop map(a->x:: int):: [int] { x + offset:: int}
+ }
+ )";
+
+ auto man = details::tier1::XreateManager::prepare(move(code));
+ LLVMLayer* llvm = man->llvm;
+ man->analyse();
+
+ std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<>(man));
+ compiler->prepare();
+ LambdaIR compilerLambda(compiler.get());
+ CodeScope* scopeLoop = man->root->findFunction("myfn")->getEntryScope()->getBody().blocks.front();
+ auto fnRaw = compilerLambda.compile(scopeLoop, "loop");
+ llvm->print();
+ llvm->initJit();
+
+ Fn2Args fn = (Fn2Args)llvm->getFunctionPointer(fnRaw);
+ ASSERT_EQ(30, fn(10, 20));
+}
+
struct Tuple3 {intmax_t a; intmax_t b; intmax_t c; };
typedef Tuple3 (*FnTuple3)();
intmax_t fn_BUG_Triple(FnTuple3 callee){
Tuple3 result = callee();
return result.a+ result.b + result.c;
}
TEST(Compilation, BUG_Triple){
std::unique_ptr<XreateManager> man(XreateManager::prepare(R"(
Tuple2 = type {int, int}.
Tuple3 = type {int, int, int}.
Tuple4 = type {int, int, int, int}.
main = function:: Tuple3; entry()
{
{1, 2, 3}
}
)"));
FnTuple3 mainFn = (FnTuple3) man->run();
intmax_t result = fn_BUG_Triple(mainFn);
ASSERT_EQ(6, result);
// ASSERT_EQ(2, result.b);
// ASSERT_EQ(3, result.c);
}
TEST(Compilation, ExteriorFns1){
std::unique_ptr<XreateManager> man(XreateManager::prepare(R"(
fn-a = function:: int; exterior() {1}
fn-b = function:: int; exterior() {2}
)"));
man->options.requireEntryFn = false;
man->run();
FnNoArgs fnA = (FnNoArgs) man->getExteriorFn("fn-a");
ASSERT_EQ(1, fnA());
FnNoArgs fnB = (FnNoArgs) man->getExteriorFn("fn-b");
ASSERT_EQ(2, fnB());
+}
+
+TEST(Compilation, LLVMAliases){
+ FILE* code = fopen("scripts/compilation/llvmaliases.xreate", "r");
+ assert(code != nullptr);
+ std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
+ man->run();
}
\ No newline at end of file
diff --git a/cpp/tests/containers.cpp b/cpp/tests/containers.cpp
index 0049586..7175642 100644
--- a/cpp/tests/containers.cpp
+++ b/cpp/tests/containers.cpp
@@ -1,327 +1,327 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* containers.cpp
*
* Created on: Jun 9, 2015
* Author: pgess <v.melnychenko@xreate.org>
*/
#include "xreatemanager.h"
#include "query/containers.h"
#include "main/Parser.h"
#include "pass/compilepass.h"
#include "llvmlayer.h"
#include "supplemental/docutils.h"
#include "supplemental/basics.h"
#include "gtest/gtest.h"
using namespace std;
using namespace xreate::grammar::main;
using namespace xreate::containers;
using namespace xreate;
struct Tuple2 {intmax_t a; intmax_t b;};
typedef Tuple2 (*FnTuple2)();
struct Tuple4 {intmax_t a; intmax_t b; intmax_t c; intmax_t d;};
typedef Tuple4 (*FnTuple4)();
TEST(Containers, RecInitByList1){
string code = R"(
Rec = type {x:: int, y:: int}.
test = function(a:: int, b::int):: Rec; entry() {
{x = a + b, y = 2}
}
)";
auto man = XreateManager::prepare(move(code));
man->run();
}
TEST(Containers, RecInitByList2){
string code = R"(
Rec = type {x:: int, y:: int}.
test = function(a:: int, b::int):: Rec; entry() {
{a + b, y = 2}
}
)";
auto man = XreateManager::prepare(move(code));
man->run();
}
TEST(Containers, RecUpdateByList1){
string code = R"(
Rec = type {x:: int, y:: int}.
test = function(a:: int, b::int):: Rec; entry() {
r = {0, y = 2}:: Rec.
r : {a + b}
}
)";
auto man = XreateManager::prepare(move(code));
man->run();
}
TEST(Containers, RecUpdateByListIndex1){
string code = R"(
Rec = type {x:: int, y:: int}.
test = function(a:: int, b::int):: int; entry() {
r1 = undef:: Rec.
r2 = r1 : {[1] = b, [0] = a}:: Rec.
r2["x"]
}
)";
auto man = XreateManager::prepare(move(code));
Fn2Args program = (Fn2Args) man->run();
ASSERT_EQ(10, program(10, 11));
}
TEST(Containers, RecUpdateInLoop1){
FILE* code = fopen("scripts/containers/RecUpdateInLoop1.xreate", "r");
assert(code != nullptr);
auto man = XreateManager::prepare(code);
Fn1Args program = (Fn1Args) man->run();
ASSERT_EQ(11, program(10));
}
TEST(Containers, ArrayInit1){
XreateManager* man = XreateManager::prepare(
R"Code(
main = function(x:: int):: int; entry() {
a = {1, 2, 3}:: [int].
a[x]
}
)Code");
void* mainPtr = man->run();
Fn1Args main = (Fn1Args) mainPtr;
ASSERT_EQ(2, main(1));
delete man;
}
TEST(Containers, ArrayUpdate1){
XreateManager* man = XreateManager::prepare(R"(
main = function(x::int):: int; entry()
{
a = {1, 2, 3}:: [int]; csize(5).
b = a : {[1] = x}:: [int]; csize(5).
b[1]
}
)");
void* mainPtr = man->run();
Fn1Args main = (Fn1Args) mainPtr;
ASSERT_EQ(2, main(2));
delete man;
}
TEST(Containers, FlyMap1){
std::unique_ptr<XreateManager> man(XreateManager::prepare(R"(
main = function:: int; entry()
{
x = {1, 2, 3, 4}:: [int].
y = loop map(x->el::int)::[int]; fly(csize(4))
{2 * el:: int }.
loop fold((y::[int]; fly(csize(4)))->el:: int, 0->sum):: int {sum + el}-20
}
)"));
FnNoArgs mainFn = (FnNoArgs) man->run();
intmax_t valueMain = mainFn();
ASSERT_EQ(0, valueMain);
}
TEST(Containers, ArrayArg1){
FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
assert(code != nullptr);
std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
man->options.requireEntryFn = false;
man->run();
FnNoArgs fnTested = (FnNoArgs) man->getExteriorFn("fn-ArrayArg1");
ASSERT_EQ(1, fnTested());
}
TEST(Containers, FlyArg1){
FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
assert(code != nullptr);
std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
man->options.requireEntryFn = false;
man->run();
FnNoArgs fnTested = (FnNoArgs) man->getExteriorFn("fn-FlyArg1");
ASSERT_EQ(8, fnTested());
}
TEST(Containers, Range1){
FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
assert(code != nullptr);
std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
man->options.requireEntryFn = false;
man->run();
{
FnNoArgs fnRange1 = (FnNoArgs) man->getExteriorFn("fn-Range1");
ASSERT_EQ(10, fnRange1());
}
{
FnNoArgs fnRange2 = (FnNoArgs) man->getExteriorFn("fn-Range2");
ASSERT_EQ(20, fnRange2());
}
}
//TEST(Containers, ListAsArray2){
// XreateManager* man = XreateManager::prepare(
//
//R"Code(
// // CONTAINERS
// import raw("scripts/dfa/ast-attachments.lp").
// import raw("scripts/containers/containers.lp").
//
// main = function:: int;entry {
// a= {1, 2, 3}:: [int].
// b= loop map(a->el:: int):: [int]{
// 2 * el
// }.
//
// sum = loop fold(b->el:: int, 0->acc):: int {
// acc + el
// }.
//
// sum
// }
//)Code");
//
// void* mainPtr = man->run();
// FnNoArgs main = (FnNoArgs) mainPtr;
// ASSERT_EQ(12, main());
//
// delete man;
//}
//
//TEST(Containers, Doc_RecField1){
// string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecField1");
// XreateManager::prepare(move(code_Variants1));
//
// ASSERT_TRUE(true);
//}
//
//TEST(Containers, Doc_RecUpdate1){
// string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecUpdate1");
// XreateManager::prepare(move(code_Variants1));
//
// ASSERT_TRUE(true);
//}
//
//TEST(Containers, ContanierLinkedList1){
// FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r");
// assert(input != nullptr);
//
// Scanner scanner(input);
// Parser parser(&scanner);
// parser.Parse();
//
// AST* ast = parser.root->finalize();
// CodeScope* body = ast->findFunction("test")->getEntryScope();
-// const Symbol symb_chilrenRaw{body->getSymbol("childrenRaw"), body};
+// const Symbol symb_chilrenRaw{body->findSymbolByAlias("childrenRaw"), body};
//
// containers::ImplementationLinkedList iLL(symb_chilrenRaw);
//
// ASSERT_EQ(true, static_cast<bool>(iLL));
// ASSERT_EQ("next", iLL.fieldPointer);
//
// Implementation impl = Implementation::create(symb_chilrenRaw);
// ASSERT_NO_FATAL_FAILURE(impl.extract<ON_THE_FLY>());
//
// ImplementationRec<ON_THE_FLY> recOnthefly = impl.extract<ON_THE_FLY>();
// ASSERT_EQ(symb_chilrenRaw, recOnthefly.source);
//}
//
//TEST(Containers, Implementation_LinkedListFull){
// FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r");
// assert(input != nullptr);
//
// std::unique_ptr<XreateManager> program(XreateManager::prepare(input));
// void* mainPtr = program->run();
// int (*main)() = (int (*)())(intptr_t)mainPtr;
//
// intmax_t answer = main();
// ASSERT_EQ(17, answer);
//
// fclose(input);
//}
//
//TEST(Containers, Doc_Intr_1){
// string example = R"Code(
// import raw("scripts/containers/containers.lp").
//
// test = function:: int; entry
// {
// <BODY>
// x
// }
// )Code";
// string body = getDocumentationExampleById("documentation/Concepts/containers.xml", "Intr_1");
// replace(example, "<BODY>", body);
//
// XreateManager* xreate = XreateManager::prepare(move(example));
// FnNoArgs program = (FnNoArgs) xreate->run();
//
// intmax_t result = program();
// ASSERT_EQ(1, result);
//}
//
//TEST(Containers, Doc_OpAccessSeq_1){
// string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessSeq_1");
// XreateManager* xreate = XreateManager::prepare(move(example));
// FnNoArgs program = (FnNoArgs) xreate->run();
//
// intmax_t result = program();
// ASSERT_EQ(15, result);
//}
//
//TEST(Containers, Doc_OpAccessRand_1){
// string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessRand_1");
// XreateManager* xreate = XreateManager::prepare(move(example));
// FnNoArgs program = (FnNoArgs) xreate->run();
//
// intmax_t result = program();
// ASSERT_EQ(2, result);
//}
//
//TEST(Containers, Doc_ASTAttach_1){
// string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "ASTAttach_1");
// string outputExpected = "containers_impl(s(1,-2,0),onthefly)";
// XreateManager* xreate = XreateManager::prepare(move(example));
//
// testing::internal::CaptureStdout();
// xreate->run();
// std::string outputActual = testing::internal::GetCapturedStdout();
//
// ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
//}
//
//TEST(Containers, IntrinsicArrInit1){
// XreateManager* man = XreateManager::prepare(
//
//R"Code(
//FnAnns = type predicate {
// entry
//}
//
//main = function(x:: int):: int; entry() {
// a{0} = intrinsic array_init(16):: [int].
// a{1} = a{0} + {15: 12}
//}
//)Code");
//}
diff --git a/cpp/tests/interpretation.cpp b/cpp/tests/interpretation.cpp
index a833453..c7d3d75 100644
--- a/cpp/tests/interpretation.cpp
+++ b/cpp/tests/interpretation.cpp
@@ -1,476 +1,476 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* interpretation.cpp
*
* Created on: -
* Author: pgess <v.melnychenko@xreate.org>
*/
#include "attachments.h"
using namespace xreate;
#include "xreatemanager.h"
#include "compilation/targetinterpretation.h"
#include "supplemental/docutils.h"
#include "gtest/gtest.h"
#include "boost/scoped_ptr.hpp"
//#define FRIENDS_INTERPRETATION_TESTS \
// friend class ::Modules_AST2_Test; \
// friend class ::Modules_Discovery1_Test; \
// friend class ::Modules_Solve1_Test;
#include "pass/interpretationpass.h"
using namespace xreate::grammar::main;
using namespace xreate::interpretation;
using namespace std;
TEST(Interpretation, Analysis_StatementIF_1) {
XreateManager* man = XreateManager::prepare(
R"Code(
main = function::bool {
x = "a":: string.
y = if (x=="b"):: bool; i12n(on()) {
true
} else {
false
}.
y
}
)Code");
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope();
- Symbol symbolY{scopeEntry->getSymbol("y"), scopeEntry};
+ Symbol symbolY{scopeEntry->findSymbolByAlias("y"), scopeEntry};
InterpretationData &dataSymbolY = Attachments::get<InterpretationData>(symbolY);
ASSERT_EQ(INTR_ONLY, dataSymbolY.resolution);
}
TEST(Interpretation, Analysis_StatementIF_InterpretCondition_1) {
XreateManager* man = XreateManager::prepare(
R"Code(
main = function(x:: int):: int {
comm= "inc":: string; i12n(on()).
y = if (comm == "inc")::int {x+1} else {x}.
y
}
)Code");
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope();
- Symbol symbolY{scopeEntry->getSymbol("y"), scopeEntry};
+ Symbol symbolY{scopeEntry->findSymbolByAlias("y"), scopeEntry};
InterpretationData &dataSymbolY = Attachments::get<InterpretationData>(symbolY);
ASSERT_EQ(CMPL_ONLY, dataSymbolY.resolution);
ASSERT_EQ(IF_INTERPRET_CONDITION, dataSymbolY.op);
}
TEST(Interpretation, StatementCall_RecursionIndirect_1) {
XreateManager* man = XreateManager::prepare(
R"Code(
searchNemo = function(data:: Data):: bool
{
if (data == "nemo"):: bool
{false} else {searchDory(data)}
}
searchDory = function(data:: Data):: bool
{
if (data == "dory"):: bool
{true} else {searchNemo(data)}
}
entry = function:: bool; entry() {
searchNemo(""):: bool; i12n(on())
}
)Code");
InterpretationPass* pass = new InterpretationPass(man);
ASSERT_DEATH(pass->run(), "Indirect recursion detected");
}
TEST(Interpretation, PartialIntr_1) {
XreateManager* man = XreateManager::prepare(
R"Code(
evaluate= function(argument:: int, code:: string; i12n(on())):: int {
switch(code):: int
case ("inc") {argument + 1}
case ("dec") {argument - 1}
case ("double") {argument * 2}
case default {argument}
}
main = function:: int; entry() {
commands= {"inc", "double", "dec"}:: [string]; i12n(on()).
loop fold(commands->comm::string, 10->operand):: int
{
evaluate(operand, comm)
}
}
)Code");
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
ManagedFnPtr fnEvaluate = man->root->findFunction("evaluate");
InterpretationResolution resFnEvaluate = pass->process(fnEvaluate);
ASSERT_EQ(CMPL_ONLY, resFnEvaluate);
ASSERT_TRUE(FunctionInterpretationHelper::needPartialInterpretation(fnEvaluate));
const Expression &exprLoop = man->root->findFunction("main")->__entry->getBody();
Symbol symbCallEv{{0, versions::VERSION_NONE}, exprLoop.blocks.front()};
InterpretationData dataCallEv = Attachments::get<InterpretationData>(symbCallEv);
ASSERT_EQ(CMPL_ONLY, dataCallEv.resolution);
ASSERT_EQ(CALL_INTERPRET_PARTIAL, dataCallEv.op);
}
TEST(Interpretation, PartialIntr_3) {
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
R"Code(
Command= type variant {INC, DEC, DOUBLE}.
evaluate= function(argument:: int, code:: Command; i12n(on())):: int {
switch variant(code)::int
case (INC) {argument + 1}
case (DEC) {argument - 1}
case (DOUBLE) {argument * 2}
}
main = function::int; entry() {
commands= {INC(), DOUBLE(), DEC()}:: [Command]; i12n(on()).
loop fold(commands->comm::Command, 10->operand):: int{
evaluate(operand, comm)
}
}
)Code");
man->analyse();
if(!man->isPassRegistered(PassId::InterpretationPass)) {
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
}
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(21, result);
}
TEST(Interpretation, PartialIntr_4) {
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
R"Code(
Command= type variant {INC, DEC, DOUBLE}.
evaluate= function(argument:: int, code:: Command; i12n(on())):: int {
switch variant(code)::int
case (INC) {argument + 1}
case (DEC) {argument - 1}
case (DOUBLE) {argument * 2}
}
main = function::int; entry() {
evaluate(4, DEC())
}
)Code");
man->analyse();
if(!man->isPassRegistered(PassId::InterpretationPass)) {
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
}
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(3, result);
}
TEST(Interpretation, SwitchVariant) {
xreate::XreateManager* man = xreate::XreateManager::prepare(
R"Code(
Command= type variant {
ADD(x::int, y::int),
DEC(x::int),
DOUBLE(x::int)
}.
main = function::int; entry(){
program = ADD(2, 3)::Command; i12n(on()).
switch variant(program)::int
case (ADD) {program["x"]+program["y"]}
case (DEC) {1}
case (DOUBLE) {2}
}
)Code");
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(5, result);
}
TEST(Interpretation, SwitchVariantAlias) {
xreate::XreateManager* man = xreate::XreateManager::prepare(
R"Code(
Command= type variant {
ADD(x::int, y::int),
DEC(x::int),
DOUBLE(x::int)
}.
main = function::int; entry(){
program = {ADD(2, 3), DEC(8)}::[Command]; i12n(on()).
switch variant(program[0]->program::Command)::int
case (ADD) {program["x"]+program["y"]}
case (DEC) {1}
case (DOUBLE) {2}
}
)Code");
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(5, result);
}
TEST(Interpretation, Multiindex1) {
std::string script2 =
R"Code(
extract = function(program::unknType)::int; i12n(on()){
program["arguments"][1]
}
main = function::int; entry() {
x = {arguments = {10, 9, 8, 7}}:: unknType; i12n(on()).
extract(x)
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(std::move(script2)));
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(9, result);
}
TEST(InterpretationExamples, Regexp1) {
FILE* input = fopen("scripts/dsl/regexp.xreate", "r");
assert(input != nullptr);
std::unique_ptr<XreateManager> man(XreateManager::prepare(input));
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(4, result);
}
TEST(Interpretation, Variant1) {
std::string script =
R"Code(
DataPacked = type variant {
Var1(Num:: [int], String::string),
Var2(Num:: [int], String::string)
}.
extractInt = function(data::DataPacked):: int {
resultWrong = 0 :: int.
switch variant(data)::int
case (Var1) {data["Num", 0]}
case (Var2) {resultWrong}
}
main = function :: int; entry() {
dataActual = {10}:: [int].
dataPacked = Var1(dataActual, "whatever"):: DataPacked.
extractInt(dataPacked):: int; i12n(on())
}
)Code";
std::unique_ptr<XreateManager> man(XreateManager::prepare(std::move(script)));
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(10, result);
}
TEST(Interpretation, Doc_Intr_1) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Intr_1");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
int (* main)() = (int (*)()) man->run();
int result = main();
ASSERT_EQ(0, result);
}
TEST(Interpretation, Doc_FnIntr_1) {
string example1 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_1");
string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_FnIntr_1");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example1));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
unsigned char (* main)() = (unsigned char (*)()) man->run();
unsigned char result = main();
ASSERT_EQ(1, result);
XreateManager* man2 = XreateManager::prepare(move(example2));
ASSERT_TRUE(true);
}
TEST(Interpretation, Doc_FnIntr_2) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_2");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
unsigned char (* main)() = (unsigned char (*)()) man->run();
unsigned char result = main();
ASSERT_EQ(1, result);
}
TEST(Interpretation, Doc_FnIntr_3) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_3");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
InterpretationResolution resolutionActual = pass->process(man->root->findFunction("unwrap"));
ASSERT_EQ(ANY, resolutionActual);
unsigned char (* main)() = (unsigned char (*)()) man->run();
unsigned char result = main();
ASSERT_NE(0, result);
}
TEST(Interpretation, Doc_LateIntr_1) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateIntr_1");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
int (* main)(int) = (int (*)(int)) man->run();
int result = main(1);
ASSERT_EQ(2, result);
}
TEST(Interpretation, Doc_LateIntr_2) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateIntr_2");
string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_LateIntr_2");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
InterpretationPass* pass;
if(man->isPassRegistered(PassId::InterpretationPass)) {
pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
} else {
pass = new InterpretationPass(man);
pass->run();
}
const ManagedFnPtr &funcMain = man->root->findFunction("main");
InterpretationData &dataBody = Attachments::get<InterpretationData>(funcMain);
ASSERT_EQ(FOLD_INTERPRET_INPUT, dataBody.op);
int (* main)(int) = (int (*)(int)) man->run();
int result = main(10);
ASSERT_EQ(21, result);
XreateManager* man2 = XreateManager::prepare(move(example2));
ASSERT_TRUE(true);
}
TEST(Interpretation, Doc_LateFnIntr_1) {
string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateFnIntr_1");
string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_LateFnIntr_1");
xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example));
man->analyse();
if(!man->isPassRegistered(PassId::InterpretationPass)) {
InterpretationPass* pass = new InterpretationPass(man);
pass->run();
}
int (* main)(int) = (int (*)(int)) man->run();
int result = main(10);
ASSERT_EQ(21, result);
XreateManager* man2 = XreateManager::prepare(move(example2));
ASSERT_TRUE(true);
}
//TOTEST call indirect recursion(w/o tags)
//TASk implement and test Loop Inf (fix acc types in coco grammar)
diff --git a/cpp/tests/polymorph.cpp b/cpp/tests/polymorph.cpp
index bc35f39..4b847bc 100644
--- a/cpp/tests/polymorph.cpp
+++ b/cpp/tests/polymorph.cpp
@@ -1,266 +1,266 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* polymorph.cpp
*
* Author: pgess <v.melnychenko@xreate.org>
* Created on October 11, 2017, 8:37 PM
*/
#include "xreatemanager.h"
#include "ast.h"
#include "transcendlayer.h"
#include "aux/latereasoning.h"
#include <list>
#include "gtest/gtest.h"
#include "query/polymorph.h"
#include "supplemental/docutils.h"
#include "supplemental/basics.h"
using namespace xreate;
using namespace xreate::latereasoning;
using namespace xreate::polymorph;
using namespace std;
TEST(Polymorphs, ast1) {
xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE(
global-ann = type predicate {
entry
}
context-ann = type predicate {
a, b
}
guard(::a) {
test = function:: int {0}
}
guard(::b) {
test = function:: int {1}
}
main = function:: int; entry() { test() }
)CODE");
const std::list<ManagedFnPtr>& specs = man->root->getFnSpecializations("test");
ASSERT_EQ(2, specs.size());
auto itSpecs = specs.begin();
ASSERT_EQ("a", (*itSpecs)->guard.getValueString());
itSpecs++;
ASSERT_EQ("b", (*itSpecs)->guard.getValueString());
}
TEST(Polymorphs, Compile1){
xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE(
global-ann = type predicate {
entry, guarded(value::int)
}
context-ann = type predicate {
first(value::int), second
}
guard(data::first) {
test = function:: int {data["value"]}
}
guard(::second) {
test = function:: int {1}
}
main = function:: int; entry()
{
x = 8:: int.
test()::int; guarded(x)
}
)CODE");
man->transcend->addRawScript(R"(func_supply_guard(Site, first(Data), "context-ann"):- bind(Site, guarded(Data)).)");
man->run();
}
TEST(Polymorphs, Compile2){
xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE(
guard-pred = type predicate {
first(value::int), second
}
global-ann = type predicate {
entry, guarded(guard-pred)
}
guard(data ::first) {
test = function:: int {data["value"]}
}
guard(::second) {
test = function:: int {1}
}
main = function:: int; entry()
{
x = first(8):: guard-pred.
test()::int; guarded(x)
}
)CODE");
man->transcend->addRawScript(R"(func_supply_guard(Site, Guard, "guard-pred"):- bind(Site, guarded(Guard)).)");
man->run();
}
TEST(Polymorphs, CompileException1){
xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE(
Global-ann = type predicate {
entry
}
HandlerDivByZero = type {
ret:: int
}
Guards = type predicate {
fast,
safe(handlerZeroDiv:: HandlerDivByZero)
}
guard(:: fast) {
div = function(a::int, b::int) { a / b}
}
guard(handlers:: safe) {
div = function(a::int, b::int)::int
{
if (b != 0):: int
{a / b} else { handlers["handlerZeroDiv", "ret"] }
}
}
main = function:: int; entry()
{
handler = { ret = 11 } ::HandlerDivByZero.
div(8, 0):: int; safe(handler)
}
)CODE");
man->transcend->addRawScript(R"(func_supply_guard(Site, Guard, "Guards"):- bind(Site, Guard).)");
FnNoArgs fn = (FnNoArgs) man->run();
int result = fn();
ASSERT_EQ(11, result);
}
//TEST(Polymorphs, PolymorphQuery_Static_1) {
// xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
// R"CODE(
// import raw("scripts/dfa/polymorphism.lp").
//
// guard:: a {
// test = function:: int {0}
// }
//
// guard:: b {
// test = function:: int {1}
// }
//
// main = function:: int; entry { test()::int; callguard(b); dfa_polym(ret)}
//
//)CODE");
//
// man->analyse();
// PolymorphQuery* query = dynamic_cast<PolymorphQuery*> (man->transcend->getQuery(QueryId::PolymorphQuery));
//
// const Expression& bodyE = man->root->findFunction("main")->getEntryScope()->getBody();
// LateAnnotation decisionLA = query->get(bodyE);
// ASSERT_EQ(1, decisionLA.guardedContent.size());
//
// auto decisionOptSymb = decisionLA.select({}, man->root, man->transcend);
// ASSERT_TRUE(decisionOptSymb);
//
// decisionOptSymb->print(cout);
// cout << endl;
// string guard = query->getValue(*decisionOptSymb).getValueString();
// ASSERT_STREQ("b", guard.c_str());
//}
//
//TEST(Polymorphs, PolymorphQuery_Late_1){
// xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
//R"CODE(
// Late = type variant{a, b}.
// main = function:: int; entry{key= a():: Late; test. key::int}
//)CODE");
//
// man->transcend->addRawScript(
//R"RULE(
// late(S, S, a, dfa_callguard(S, a)):-
// bind(S, test).
//
// late(S, S, b, dfa_callguard(S, b)):-
// bind(S, test).
//)RULE");
// man->analyse();
// PolymorphQuery* query = dynamic_cast<PolymorphQuery*> (man->transcend->getQuery(QueryId::PolymorphQuery));
//
// CodeScope* scopeMain = man->root->findFunction("main")->getEntryScope();
-// Symbol keyS = Symbol{scopeMain->getSymbol("key"), scopeMain};
+// Symbol keyS = Symbol{scopeMain->findSymbolByAlias("key"), scopeMain};
//
// Expression keyE = scopeMain->getDefinition(keyS);
// latereasoning::LateAnnotation answerLA = query->get(keyE);
// Expression valueB(Operator::VARIANT, {}); valueB.setValueDouble(1);
// auto answerRaw = answerLA.select({valueB}, man->root, man->transcend);
// ASSERT_TRUE(answerRaw);
// Expression answerE = query->getValue(*answerRaw);
// ASSERT_STREQ("b", answerE.getValueString().c_str());
//}
//
//TEST(Polymorphs, PSCU_1){
// auto man = details::tier1::XreateManager::prepare(R"Code(
// Dom = type variant {guard1, guard2}.
//
// guard:: guard1 {
// compute = function :: int
// {0}
// }
//
// guard:: guard2 {
// compute = function :: int
// {1}
// }
//
// test = function:: int; entry
// {
// xLate = guard2():: Dom.
// y1= switch late (xLate:: Dom; alias(xLate)):: int
// {
// compute():: int; guardkey(xLate)
// }.
// y1
// }
// )Code");
//
// man->transcend->addRawScript(R"RAW(
// dom(guard1; guard2).
// late(Target, Key, Variant, dfa_callguard(Target, Variant)):-
// bind(Target, guardkey(Alias));
// bind(Key, alias(Alias));
// dom(Variant).
// )RAW");
// man->analyse();
// int (*program)() = (int (*)())man->run();
// int result = program();
//
// ASSERT_EQ(1, result);
//}
//
//TEST(Polymorphs, Doc_FnLvlPoly_1){
// string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "FnLvlPoly_1");
// auto man = XreateManager::prepare(move(example));
// ASSERT_TRUE(true);
//}
//
//TEST(Polymorphs, Doc_LatePoly_1){
// string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "LatePoly_1");
// auto man = XreateManager::prepare(move(example));
// ASSERT_TRUE(true);
//}
\ No newline at end of file
diff --git a/cpp/tests/types.cpp b/cpp/tests/types.cpp
index 5592981..05f671f 100644
--- a/cpp/tests/types.cpp
+++ b/cpp/tests/types.cpp
@@ -1,311 +1,311 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* types.cpp
*
* Created on: Jun 4, 2015
* Author: pgess <v.melnychenko@xreate.org>
*/
#include "gtest/gtest.h"
#include "xreatemanager.h"
#include "llvmlayer.h"
#include "main/Parser.h"
#include "transcendlayer.h"
#include "analysis/typeinference.h"
#include "analysis/utils.h"
using namespace std;
using namespace xreate;
using namespace xreate::grammar::main;
TEST(Types, DependantTypes1) {
string&& code = "XmlNode = type {\n"
" tag:: string,\n"
" attrs:: [string], \n"
" content:: string\n"
"}.\n";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typeXmlNode = program->root->findType("XmlNode");
ASSERT_EQ(TypeOperator::RECORD, typeXmlNode->__operator);
ASSERT_EQ(3, typeXmlNode->__operands.size());
ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(0).__value);
ASSERT_EQ(TypeOperator::ARRAY, typeXmlNode->__operands.at(1).__operator);
ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(2).__value);
}
TEST(Types, ast_ParameterizedTypes_FeatureTypeIndex_1) {
string&& code = "XmlNode = type {\n"
" tag:: string,\n"
" attrs:: [string],\n"
" content:: string\n"
"}.\n"
""
"Template = type(Leaf) {Leaf, [Leaf[\"content\"]]}."
"Concrete = type Template(XmlNode).";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typeConcrete = program->root->findType("Concrete");
ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operator);
ASSERT_EQ(2, typeConcrete->__operands.size());
ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operands.at(0).__operator);
ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator);
ASSERT_EQ(TypePrimitive::String, typeConcrete->__operands.at(1).__operands.at(0).__value);
}
TEST(Types, TreeType1) {
string&& code = "XmlNode = type {\n"
" tag:: string,\n"
" attrs:: [string],\n"
" content:: string\n"
"}.\n"
""
"Tree = type(Leaf) {Leaf, [Tree(Leaf)]}."
"Concrete = type Tree(XmlNode).";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typeConcrete = program->root->findType("Concrete");
ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operator);
ASSERT_EQ(2, typeConcrete->__operands.size());
ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operands.at(0).__operator);
ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator);
auto typeLink = typeConcrete->__operands.at(1).__operands.at(0);
// ASSERT_EQ(TypeOperator::LINK, typeLink.__operator);
// ASSERT_EQ(typeConcrete->linkId, typeLink.linkId);
}
TEST(Types, TreeType1LLvm) {
string&& code = "XmlNode = type {\n"
" tag:: string,\n"
" /* attrs:: [string],*/\n"
" content:: string\n"
"}.\n"
""
"Tree = type(Leaf) {Leaf, [Tree(Leaf)]}."
"Concrete = type Tree(XmlNode).";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typeConcrete = program->root->findType("Concrete");
llvm::Type* raw = program->llvm->toLLVMType(typeConcrete);
}
TEST(Types, ArrayOfExternal1) {
FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r");
assert(input != nullptr);
Scanner scanner(input);
Parser parser(&scanner);
parser.Parse();
AST* ast = parser.root->finalize();
CodeScope* body = ast->findFunction("test")->getEntryScope();
- const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("childrenRaw")));
+ const ExpandedType& t2 = ast->getType(body->getDefinition(body->findSymbolByAlias("childrenRaw")));
EXPECT_EQ(t2->__operator, TypeOperator::ARRAY);
}
TEST(Types, ExternType1) {
FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r");
assert(input != nullptr);
Scanner scanner(input);
Parser parser(&scanner);
parser.Parse();
AST* ast = parser.root->finalize();
CodeScope* body = ast->findFunction("test")->getEntryScope();
- const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("tree")));
+ const ExpandedType& t2 = ast->getType(body->getDefinition(body->findSymbolByAlias("tree")));
EXPECT_EQ(t2->__operator, TypeOperator::ALIAS);
}
TEST(Types, ast_VariantType1) {
string&& code =
" colors = type variant {RED, BLUE, GREEN}.\n"
" test = function:: colors; entry {GREEN()}";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typ = program->root->findType("colors");
EXPECT_EQ(TypeOperator::VARIANT, typ->__operator);
Expression eRed = program->root->findFunction("test")->getEntryScope()->getBody();
EXPECT_EQ(Operator::VARIANT, eRed.op);
const ExpandedType& typ2 = program->root->getType(eRed);
EXPECT_EQ(TypeOperator::VARIANT, typ2->__operator);
}
TEST(Types, full_VariantType_Switch1) {
string&& code =
"colors = type variant{RED, BLUE, GREEN}. \n"
" test = function:: colors {GREEN()} \n"
"main = function:: int; entry { \n"
" switch(test()):: int \n"
" case (GREEN()) {0} \n"
" case default {1} \n"
"}";
XreateManager* man = XreateManager::prepare(move(code));
int (*main)() = (int (*)()) man->run();
EXPECT_EQ(0, main());
}
TEST(Types, ast_VariantType2) {
std::string script =
R"Code(
Annotation = type
variant {
Num(int),
String(string),
Func(name::string, arguments::[Expression])
}.
)Code";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(script)));
ExpandedType typ = program->root->findType("Annotation");
ASSERT_EQ(3, typ.get().fields.size());
}
TEST(Types, SlaveTypes_UnwrapSlaveType1) {
auto man = details::tier1::XreateManager::prepare(R"Code(
AtomNumT = type slave atomNumT.
AtomStrT = type slave atomStrT.
CmpndIntStrT = type slave cmpndIntStrT.
VariantT = type slave variantT.
VariantComplicatedT = type slave variantComplicatedT.
)Code");
man->transcend->addRawScript(R"RAW(
atomNumT(5). atomNumT(8).
atomStrT("a"). atomStrT("b").
cmpndIntStrT(1, "a").
cmpndIntStrT(2, "b").
variantT(first).
variantT(second).
variantT(third).
variantComplicatedT(first(1, "a")).
variantComplicatedT(second("b")).
)RAW");
man->analyse();
ExpandedType AtomNumT = man->root->findType("AtomNumT");
ASSERT_EQ(AtomNumT->__operator, TypeOperator::SLAVE);
ExpandedType ContentAtomNumT = analysis::dereferenceSlaveType(AtomNumT, man->transcend);
ASSERT_EQ(TypePrimitive::Int, ContentAtomNumT->__value);
ExpandedType AtomStrT = man->root->findType("AtomStrT");
ExpandedType ContentAtomStrT = analysis::dereferenceSlaveType(AtomStrT,
man->transcend);
ASSERT_EQ(TypePrimitive::String, ContentAtomStrT->__value);
ExpandedType CmpndIntStrT = man->root->findType("CmpndIntStrT");
ExpandedType ContentCmpndIntStrT = analysis::dereferenceSlaveType(CmpndIntStrT,
man->transcend);
ASSERT_EQ(TypeOperator::RECORD, ContentCmpndIntStrT->__operator);
ASSERT_EQ(2, ContentCmpndIntStrT->__operands.size());
ExpandedType VariantT = man->root->findType("VariantT");
ExpandedType ContentVariantT = analysis::dereferenceSlaveType(VariantT,
man->transcend);
ASSERT_EQ(TypeOperator::VARIANT, ContentVariantT->__operator);
ASSERT_EQ(3, ContentVariantT->fields.size());
ExpandedType VariantComplicatedT = man->root->findType("VariantComplicatedT");
ExpandedType ContentVariantComplicatedT = analysis::dereferenceSlaveType(VariantComplicatedT,
man->transcend);
ASSERT_EQ(TypeOperator::VARIANT, ContentVariantComplicatedT->__operator);
ASSERT_EQ(2, ContentVariantComplicatedT->fields.size());
ASSERT_EQ(2, ContentVariantComplicatedT->__operands.at(0).__operands.size());
}
TEST(Types, IndexNumber_1)
{
string&& code =
R"CODE(
Tuple = type {string, int}.
Int = type Tuple[1].
)CODE";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typeInt = program->root->findType("Int");
}
TEST(Types, RecursiveTypes_1){
string&& code =
R"CODE(
Node = type {
data:: int,
next:: ref(Node)
}
)CODE";
std::string outputExpected = "{ i32, %Node* }";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typNode = program->root->findType("Node");
llvm::Type* typNodeRaw = program->llvm->toLLVMType(typNode);
std::string outputActual;
llvm::raw_string_ostream os(outputActual);
typNodeRaw->print(os);
os.flush();
ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}
TEST(Types, RecursiveTypes_2){
string&& code =
R"CODE(
Tree = type(NodeData) variant {
LEAF(data:: NodeData),
GROUP(nodes:: [ref(Tree(NodeData))])
}
Guard = type variant {
a, b
}.
DecisionTree = type Tree(Guard).
)CODE";
std::string outputExpected = "{ i8, { { i8 } } ";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
ExpandedType typNode = program->root->findType("DecisionTree");
llvm::Type* typNodeRaw = program->llvm->toLLVMType(typNode);
std::string outputActual;
llvm::raw_string_ostream os(outputActual);
typNodeRaw->print(os);
os.flush();
cout << outputActual << endl;
ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}
TEST(Types, NestedList1){
string code = R"(
test = function:: int; entry()
{
x = {a = {b = 3}}:: {a:: {b:: int}}.
x["a", "b"]
}
)";
std::unique_ptr<XreateManager> program(XreateManager::prepare(move(code)));
program->run();
}
\ No newline at end of file
diff --git a/grammar/xreate.ATG b/grammar/xreate.ATG
index 307d0a6..80c548f 100644
--- a/grammar/xreate.ATG
+++ b/grammar/xreate.ATG
@@ -1,836 +1,837 @@
//TODO add ListLiteral
//TODO ExprTyped: assign default(none) type
#include "ast.h"
#include "ExternLayer.h"
#include <string>
#include <stack>
#include <sstream>
#define wprintf(format, ...) \
char __buffer[100]; \
wcstombs(__buffer, format, 100); \
fprintf(stderr, __buffer, __VA_ARGS__)
using namespace std;
COMPILER Xreate
details::inconsistent::AST* root = nullptr; // current program unit
void SemErr(std::initializer_list<std::wstring> msgs){
std::wstringstream output;
for(const auto& msg: msgs){output << msg;}
SemErr(output.str().c_str());
}
void ensureInitalizedAST(){
if (root == nullptr) root = new details::inconsistent::AST();
}
struct {
std::stack<CodeScope*> scopesOld;
CodeScope* scope = nullptr;
} context;
void pushContextScope(CodeScope* scope){
context.scopesOld.push(context.scope);
context.scope = scope;
}
void popContextScope(){
context.scope = context.scopesOld.top();
context.scopesOld.pop();
}
int nextToken()
{
scanner->ResetPeek();
return scanner->Peek()->kind;
}
bool checkTokenAfterIdent(int key){
if (la->kind != _ident) return false;
return nextToken() == key;
}
bool checkParametersList()
{
return la->kind == _ident && nextToken() == _lparen;
}
bool checkInfix()
{
return la->kind == _ident && nextToken() == _ident;
}
bool checkIndex()
{
return la->kind == _ident && nextToken() == _lbrack;
}
bool checkListIndex()
{
return la->kind == _lcurbrack && nextToken() == _lbrack;
}
bool checkFuncDecl()
{
if (la->kind != _ident) return false;
int token2 = nextToken();
int token3 = scanner->Peek()->kind;
return token2 == _assign && token3 == _function;
}
bool checkAssignment()
{
if (la->kind != _ident) return false;
scanner->ResetPeek();
int token2 = scanner->Peek()->kind;
if (token2 == _lcurbrack) {
scanner->Peek();
int token3 = scanner->Peek()->kind;
if (token3 != _rcurbrack) return false;
int token4 = scanner->Peek()->kind;
return token4 == _assign;
}
return token2 == _assign;
}
void recognizeIdentifier(Expression& id, const std::wstring& hint){
if (!context.scope)
SemErr({L"Identifier found in undefined scope: ", hint});
if (!context.scope->recognizeIdentifier(id)){
root->postponeIdentifier(context.scope, id);
}
}
enum SwitchKind{SWITCH_NORMAL, SWITCH_META};
CHARACTERS
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
any = ANY - '"'.
digit = "0123456789".
cr = '\r'.
lf = '\n'.
tab = '\t'.
TOKENS
ident = (letter ['-' letter] | '_') {letter ['-' letter] | digit | '_' }.
number = digit{digit}.
string = '"' { any } '"'.
function = "function".
comma = ','.
period = '.'.
lparen = '('.
rparen = ')'.
lbrack = '['.
rbrack = ']'.
lcurbrack = '{'.
rcurbrack = '}'.
equal = "==".
assign = '='.
implic = '-' '>'.
colon = ':'.
tagcolon = "::".
lse = "<=".
lss = "<".
gte = ">=".
gtr = ">".
ne1 = "!=".
ne2= "<>".
COMMENTS FROM "/*" TO "*/" NESTED
COMMENTS FROM "//" TO lf
IGNORE cr + lf + tab
PRODUCTIONS
Xreate = (. Function* function; ensureInitalizedAST(); .)
{( //RuleDecl
InterfaceData | Imprt | GuardSection
| IF(checkFuncDecl()) FDecl<function> (. root->add(function); .)
| TDecl
| SkipModulesSection
)} (. .)
.
Ident<std::wstring& name>=
ident (. name = t->val; .).
// recognition
IdentR<Expression &e> = (. std::wstring name; .)
Ident<name> (. e = Expression(Atom<Identifier_t>(name)); .)
(. recognizeIdentifier(e, name); .).
//versioning
IdentV<Expression& e>= (. std::wstring name; .)
Ident<name> (. e = Expression(Atom<Identifier_t>(name)); .)
[ Version<e> ].
//recognition + versioning
IdentVR<Expression& e>= (. std::wstring name; .)
Ident<name> (. e = Expression(Atom<Identifier_t>(name)); .)
[ Version<e> ] (. recognizeIdentifier(e, name); .)
.
Version<Expression &e>=
lcurbrack (
ident (. SemErr({L"var version as ident is not implemented yet"}); .)
| number (. Attachments::put<versions::VariableVersion>(e, Atom<Number_t>(t->val).get()); .)
) rcurbrack .
FDecl<Function*& f> = (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; Expression binding; .)
Ident<fname> assign
function (. f = new Function(fname); CodeScope* entry = f->getEntryScope(); .)
[lparen Ident<argName> tagcolon ExprAnnotations<binding> (. f->addBinding(Atom<Identifier_t>(argName), move(binding)); .)
{comma Ident<argName> tagcolon ExprAnnotations<binding> (. f->addBinding(Atom <Identifier_t>(argName), move(binding));.)
} rparen]
tagcolon Type<typOut> {';' FnTag<f> }
BDecl<entry> (. const_cast<Expression&>(entry->getBody()).bindType(move(typOut));.)
.
GuardSection<>= (. std::wstring arg, guardI; Expression guardE, guardBinding; Function* f; TypeAnnotation guardT; .)
"guard" lparen
[Ident<arg>] tagcolon Ident<guardI> (. guardE = Expression(Operator::CALL, {Atom<Identifier_t>(guardI)}); bool res = root->recognizeVariantConstructor(guardE); .)
(. if(!res) SemErr(coco_string_create("Can't recognize a guard"));.)
(. if (!arg.empty()) guardE.addBindings({Atom<Identifier_t>(arg)}); .)
(. guardBinding.type = TypeAnnotation(TypeOperator::GUARD, {guardE.type}); guardBinding.type.__valueCustom = Atom<Identifier_t>(guardI).get(); .)
rparen lcurbrack {
FDecl<f> (. f->guard = guardE; if (!arg.empty()){f->addBinding(Atom<Identifier_t>(arg), Expression(guardBinding));} .)
(. root->add(f); .)
} rcurbrack
.
/**
* TYPES
*
*/
TypeTerm<TypePrimitive& typ> = (. std::wstring tid; .)
( "string" (. typ = TypePrimitive::String;.)
| "int" (. typ = TypePrimitive::Int;.)
| "float" (. typ = TypePrimitive::Float;.)
| "bool" (. typ = TypePrimitive::Bool; .)
| "i8" (. typ = TypePrimitive::I8; .)
| "i32" (. typ = TypePrimitive::I32; .)
| "i64" (. typ = TypePrimitive::I64; .)
).
Type<TypeAnnotation& typ> = (. TypeAnnotation typ2; TypePrimitive typ3; std::wstring tid; std::string field; .)
(
TList<typ>
| TRecord<typ>
| TVariant<typ>
| TPred<typ>
| TSlave<typ>
| TRef<typ>
| TypeTerm<typ3> (. typ = typ3; .)
| IF (checkIndex()) Ident<tid> lbrack
TypeIndex<field> (. typ = TypeAnnotation(TypeOperator::ACCESS, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); typ.fields.push_back(field); .)
{comma TypeIndex<field> (. typ.fields.push_back(field); .)
} rbrack
| Ident<tid> (. typ = TypeAnnotation(TypeOperator::ALIAS, {}); typ.__valueCustom = Atom<Identifier_t>(tid).get(); .)
[lparen Type<typ2> (. typ.__operands.push_back(typ2); .)
{comma Type<typ2> (. typ.__operands.push_back(typ2); .)
} rparen]
| '*' (.typ = TypeAnnotation(); .)
) .
TypeIndex<std::string& name> =
(
number (. name = Atom<Identifier_t>(t->val).get(); .)
| string (. name = Atom<String_t>(t->val).get(); .)
)
.
TList<TypeAnnotation& typ> = (. TypeAnnotation ty; .)
lbrack Type<ty> rbrack (. typ = TypeAnnotation(TypeOperator::ARRAY, {ty}); .)
.
TRecordBody<TypeAnnotation& typ> =
(. TypeAnnotation t; std::wstring key; size_t keyCounter=0;
typ = TypeAnnotation(TypeOperator::RECORD, {});
.)
{
(
IF(checkTokenAfterIdent(_tagcolon)) Ident<key> tagcolon
| (. key = to_wstring(keyCounter++); .)
)
Type<t> [comma] (. typ.__operands.push_back(t); .)
(. typ.fields.push_back(Atom<Identifier_t>(key).get()); .)
}.
TRecord<TypeAnnotation& typ> =
lcurbrack TRecordBody<typ> rcurbrack
(. if(!typ.__operands.size()) SemErr(coco_string_create("Record type can't be empty.")); .)
.
TVariantRec<TypeAnnotation& typ> = (. TypeAnnotation typVoid; .)
lparen TRecordBody<typ> rparen
(. if(typ.__operands.size()==0) typ = typVoid; .)
.
TVariantBody<TypeAnnotation& typ> = (. TypeAnnotation t, typVoid; std::vector<TypeAnnotation> operands; std::vector<Atom<Identifier_t>> keys; std::wstring v; .)
lcurbrack
{ (. t = typVoid; .)
Ident<v> [TVariantRec<t>] (. keys.push_back(Atom<Identifier_t>(v)); operands.push_back(t); .)
[comma]
}
rcurbrack
(. typ = TypeAnnotation(TypeOperator::VARIANT, {});
typ.__operands = operands;
typ.addFields(std::move(keys));
.)
.
TVariant<TypeAnnotation& typ>=
"variant" TVariantBody<typ>
(. if(!typ.__operands.size()) SemErr(coco_string_create("Variant type can't be empty.")); .)
.
TPred<TypeAnnotation& typ>=
"predicate" TVariantBody<typ>
(. if(!typ.__operands.size()) SemErr(coco_string_create("Predicate type can't be empty.")); .)
.
TSlave<TypeAnnotation& typ>=
"slave" (. typ = TypeAnnotation(TypeOperator::SLAVE, {}); .)
lparen
string (. typ.__valueCustom = Atom<String_t>(t->val).get(); .)
rparen
.
TRef<TypeAnnotation& typ>= (. TypeAnnotation typChild; .)
"ref" lparen Type<typChild> rparen (. typ = TypeAnnotation(TypeOperator::REF, {typChild}); .)
.
TDecl = (. TypeAnnotation t; std::wstring tname, arg; std::vector<Atom<Identifier_t>> args; .)
Ident<tname> assign "type"
[lparen Ident<arg> (. args.push_back(Atom<Identifier_t>(arg)); .)
{comma Ident<arg> (. args.push_back(Atom<Identifier_t>(arg)); .)
} rparen]
Type<t>[period] (. t.addBindings(move(args)); root->add(move(t), Atom<Identifier_t>(tname)); .)
.
ContextDecl<CodeScope * scope> = (. Expression tag; .)
"context" tagcolon
MetaSimpExpr<tag> (. scope->tags.push_back(tag); .)
{';' MetaSimpExpr<tag> (. scope->tags.push_back(tag); .)
}.
VDecl<CodeScope* f> = (. Expression var, value;.)
IdentV<var> assign ExprTyped<value> (. Symbol identSymbol = f->addDefinition(move(var), move(value));
Attachments::put<ExprAlias_A>(value, identSymbol);
.)
.
BDecl<CodeScope* scope> = lcurbrack (. Expression body; pushContextScope(scope); bool flagBodyFound = false; .)
{(IF(checkAssignment()) VDecl<scope> period
// | RuleContextDecl<scope>
| ContextDecl<scope>period
| ExprTyped<body> (. scope->setBody(body); flagBodyFound = true; Attachments::put<ExprAlias_A>(body, Symbol{ScopedSymbol::RetSymbol, scope});.)
)}
rcurbrack (. if(!flagBodyFound) SemErr(coco_string_create("Code block with an empty body!"));
popContextScope();
.)
.
IfDecl<Expression& e> =
(. Expression cond(Operator::AND, {}), condPart;
ManagedScpPtr blockTrue = root->add(new CodeScope(context.scope));
ManagedScpPtr blockFalse = root->add(new CodeScope(context.scope));
e = Expression(Operator::IF, {});
.)
"if" lparen Expr<condPart> (. cond.operands.push_back(condPart); .)
{ comma Expr<condPart> (. cond.operands.push_back(condPart); .)
} rparen (. e.operands.push_back(cond); .)
tagcolon ExprAnnotations<e>
BDecl<&*blockTrue> "else" BDecl<&*blockFalse> (. e.addBlock(blockTrue); e.addBlock(blockFalse); .)
.
LoopDecl<Expression& e> =
(.
Expression eIn, eAcc, eFilters; std::wstring varEl, varAcc, contextClass; Expression tagsEl;
ManagedScpPtr block = root->add(new CodeScope(context.scope));
+ block->trackExternalSymbs = true;
.)
"loop"
(
"map" lparen Expr<eIn> implic Ident<varEl>
(. e = Expression(Operator::MAP, {eIn}); .)
tagcolon ExprAnnotations<tagsEl> rparen tagcolon ExprAnnotations<e>
(.
e.addBindings({Atom<Identifier_t>(varEl)});
block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl));
.)
BDecl<&*block>
(. e.addBlock(block); .)
| "fold" lparen Expr<eIn> implic Ident<varEl> tagcolon ExprAnnotations<tagsEl>
['|' Expr<eFilters> ] comma Expr<eAcc> implic Ident<varAcc>rparen
(.
e = Expression(Operator::FOLD, {eIn, eAcc});
e.addBindings({Atom<Identifier_t>(varEl), Atom<Identifier_t>(varAcc)});
.)
tagcolon ExprAnnotations<e>
(.
Expression varAccBindingE; varAccBindingE.type = e.type;
block->addBinding(Atom<Identifier_t>(varEl), move(tagsEl));
block->addBinding(Atom<Identifier_t>(varAcc), move(varAccBindingE));
.)
BDecl<&*block>
(. e.addBlock(block); .)
| lparen Expr<eAcc> implic Ident<varAcc> rparen
(.
e = Expression(Operator::FOLD_INF, {eAcc});
e.addBindings({Atom<Identifier_t>(varAcc)});
.)
tagcolon ExprAnnotations<e>
(.
Expression varAccBindingE; varAccBindingE.type = e.type;
block->addBinding(Atom<Identifier_t>(varAcc), move(varAccBindingE));
.)
BDecl<&*block>
(. e.addBlock(block); .)
).
// Switches
SwitchDecl<Expression& eSwitch, SwitchKind flagSwitchKind> = (. TypeAnnotation typ; eSwitch = Expression(Operator::SWITCH, {}); Expression eCondition; Expression tag;.)
"switch"
(
SwitchVariantDecl<eSwitch>
| SwitchLateDecl<eSwitch>
| lparen ExprTyped<eCondition> rparen tagcolon ExprAnnotations<eSwitch> (. eSwitch.operands.push_back(eCondition);.)
CaseDecl<eSwitch, flagSwitchKind> {CaseDecl<eSwitch, flagSwitchKind>}
)
.
CaseDecl<Expression& outer, SwitchKind flagSwitchKind> = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); Expression condition; .)
"case"
( IF(flagSwitchKind == SWITCH_META)
lparen MetaSimpExpr<condition> rparen BDecl<&*scope> (. Expression exprCase(Operator::CASE, {}); exprCase.addTags({condition}); exprCase.addBlock(scope); outer.addArg(move(exprCase));.)
| "default" BDecl<&*scope> (. Expression exprCase(Operator::CASE_DEFAULT, {});
exprCase.addBlock(scope);
outer.operands.insert(++outer.operands.begin(), exprCase); .)
| lparen CaseParams<&*scope> rparen (. ManagedScpPtr scopeBody = root->add(new CodeScope(&*scope)); Expression exprCase(Operator::CASE, {}); .)
BDecl<&*scopeBody> (. exprCase.addBlock(scope); exprCase.addBlock(scopeBody); outer.addArg(move(exprCase)); .)
).
CaseParams<CodeScope* scope> = (. Expression condition; Expression guard(Operator::LOGIC_AND, {}); pushContextScope(scope); .)
ExprTyped<condition> (. guard.addArg(Expression(condition)); .)
{comma ExprTyped<condition> (. guard.addArg(Expression(condition)); .)
} (. scope->setBody(guard); popContextScope(); .)
.
SwitchLateDecl<Expression& expr> =
(.
std::wstring aliasCondition; Expression exprCondition, aliasAnns;
expr = Expression(Operator::SWITCH_LATE, {});
ManagedScpPtr scope = root->add(new CodeScope(context.scope));
.)
"late" lparen Expr<exprCondition> [implic Ident<aliasCondition>] [tagcolon ExprAnnotations<aliasAnns>] rparen
tagcolon ExprAnnotations<expr> BDecl<&*scope>
(.
expr.addArg(Expression(exprCondition));
expr.addBlock(scope);
std::string alias;
if(aliasCondition.empty()){
if(exprCondition.__state != Expression::IDENT){
SemErr(coco_string_create("An identifier expected in the short form"));
return;
}
//Use exprCondition as identifier
alias = exprCondition.getValueString();
} else {
//Use aliasCondition
alias = Atom<Identifier_t>(move(aliasCondition)).get();
}
expr.addBindings({Atom<Identifier_t>(string(alias))});
scope->addBinding(Atom<Identifier_t>(move(alias)), move(aliasAnns));
.)
.
SwitchVariantDecl<Expression& expr> =
(. Expression varTested; std::wstring varAlias; bool flagAliasFound = false; expr = Expression(Operator::SWITCH_VARIANT, {}); .)
"variant" lparen Expr<varTested> [implic Ident<varAlias>
(. flagAliasFound = true; .)
] [tagcolon ExprAnnotations<varTested>] rparen tagcolon ExprAnnotations<expr>
(. expr.addArg(std::move(varTested));
if (flagAliasFound) {
expr.addBindings({Atom<Identifier_t>(varAlias)});
} else {
if(varTested.__state == Expression::IDENT){
expr.addBindings({Atom<Identifier_t>(string(varTested.getValueString()))});
}
}
.)
CaseVariantDecl<expr> {CaseVariantDecl<expr>}
.
CaseVariantDecl<Expression& expr> = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); std::wstring key; scope->addBinding(Atom<Identifier_t>(string(expr.bindings.front())), Expression()); .)
"case" lparen Ident<key> rparen (. expr.addArg(root->recognizeVariantConstructor(Atom<Identifier_t>(std::move(key)))); .)
BDecl<&*scope> (. expr.addBlock(scope); .)
.
IntrinsicDecl<Expression& outer>= (. std::wstring name; .)
"intrinsic"
(
Ident< name>
(. outer = Expression(Operator::CALL_INTRINSIC, {});
outer.setValue(Atom<Identifier_t>(name));
root->recognizeIntrinsic(outer);
.)
lparen [CalleeParams<outer>] rparen
| "query" (. outer = Expression(Operator::QUERY, {}); .)
(
"late" IntrinsicQueryLateDecl<outer>
| lparen [CalleeParams<outer>] rparen
)
).
IntrinsicQueryLateDecl<Expression& expr> =
(.
std::wstring predicateAlias; Expression predicateE, predicateAnns;
expr = Expression(Operator::QUERY_LATE, {});
ManagedScpPtr scope = root->add(new CodeScope(context.scope));
.)
lparen Expr<predicateE> implic Ident<predicateAlias> tagcolon ExprAnnotations<predicateAnns> rparen
tagcolon ExprAnnotations<expr> BDecl<&*scope>
(.
expr.addArg(move(predicateE));
expr.addBindings({Atom<Identifier_t>(wstring(predicateAlias))});
scope->addBinding(Atom<Identifier_t>(move(predicateAlias)), move(predicateAnns));
expr.addBlock(scope);
.)
.
SequenceDecl<Expression& sequence> = (. sequence = Expression(); sequence.setOp(Operator::SEQUENCE); ManagedScpPtr scope = root->add(new CodeScope(context.scope)); .)
"seq" BDecl<&*scope> (. sequence.blocks.push_back(&*scope); scope = root->add(new CodeScope(&*scope)); .)
{ (. scope = root->add(new CodeScope(&*scope)); .)
BDecl<&*scope> (. sequence.blocks.push_back(&*scope); .)
}.
/*============================ INTERFACES ===============================*/
Imprt<> =
"import" "raw" lparen string (. root->__rawImports.push_back(Atom<String_t>(t->val).get()); .)
rparen period.
InterfaceData<> = "interface" lparen
( "dfa" rparen InterfaceDFA
// | "extern-c" rparen InterfaceExternC
| "cfa" rparen InterfaceCFA
).
// InterfaceExternC<> = (. ExternData data; .)
// lcurbrack {ExternHeadersDecl<data> | ExternAliasDecl<data> } rcurbrack
// (. root->addExternData(move(data)); .)
// .
//
// ExternPkgDecl<std::wstring& package> =
// "pkgconfig" lparen
// string (. package = t->val.)
// rparen
// .
//
// ExternAliasDecl<ExternData& data> = (. std::wstring alias, package; .)
// Ident<alias> assign "library" lparen ExternPkgDecl<package> rparen period
// (. data.addLibAlias(Atom<Identifier_t>(alias), Atom<String_t>(package)); .)
// .
//
// ExternHeadersDecl<ExternData& data> = (. std::list<std::string> listInc; std::wstring& package; .)
// "include"
// [lparen
// (
// Ident<alias> (. data.requireLibAlias(Atom<Identifier_t>(alias)); .)
// | ExternPkgDecl<package> (. data.requireLibPackage(Atom<String_t>(package)); .)
// )
// rparen]
// lcurbrack { string (. listInc.push_back(Atom<String_t>(t->val).get()); .)
// [comma] } rcurbrack [period] (. data.requireHeaders(listInc); .)
// .
InterfaceDFA<> = lcurbrack { InstructDecl } rcurbrack .
InstructDecl = (.Operator op; Expression tag;
Expression scheme;
std::vector<Expression>& tags = scheme.operands;
tags.push_back(Expression()); /* return value */ .)
"operator" InstructAlias<op> tagcolon lparen (.scheme.setOp(op); .)
[
MetaSimpExpr<tag> (. tags.push_back(tag); .)
{
comma MetaSimpExpr<tag> (. tags.push_back(tag); .)
}
] rparen [ implic MetaSimpExpr<tag> (. tags[0] = tag; .)
] (. root->addDFAData(move(scheme)); .)
period.
InstructAlias<Operator& op> =
(
"map" (. op = Operator::MAP; .)
| "list_range" (. op = Operator::LIST_RANGE; .)
| "list" (. op = Operator::LIST; .)
| "fold" (. op = Operator::FOLD; .)
| "index" (. op = Operator::INDEX; .)
).
InterfaceCFA<> = lcurbrack { InstructCFADecl } rcurbrack .
InstructCFADecl<> = (.Operator op; Expression tag;
Expression scheme;
std::vector<Expression>& tags = scheme.operands; .)
"operator" InstructAlias<op> tagcolon (. scheme.setOp(op); .)
[
MetaSimpExpr<tag> (. tags.push_back(tag); .)
{
comma MetaSimpExpr<tag> (. tags.push_back(tag); .)
}
] period (. root->addInterfaceData(CFA, move(scheme)); .).
/*============================ METAPROGRAMMING ===============================*/
// TagsDecl<CodeScope* f> = (. Expression tag; TagModifier mod = TagModifier::NONE; .)
// ':' { MetaSimpExpr<tag> (. /*f.addTag(std::move(tag), mod); */ .)
// }.
FnTag<Function* f> = (. Expression tag; TagModifier mod = TagModifier::NONE; .)
MetaSimpExpr<tag>
['-' TagMod<mod>] (. f->addTag(std::move(tag), mod); .).
TagMod<TagModifier& mod> =
( "assert" (. mod = TagModifier::ASSERT; .)
| "require" (. mod = TagModifier::REQUIRE; .)
).
// RuleDecl<> =
// "rule" tagcolon (. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .)
// lparen Ident<arg> tagcolon Domain<typ> (. args.add(arg, typ); .)
// {comma Ident<arg> tagcolon Domain<typ> (. args.add(arg, typ); .)
// } rparen
// ["case" RGuard<guards> {comma RGuard<guards>}]
// lcurbrack RBody<args, guards> rcurbrack .
/* - TODO use RGuard for guards-*/
// RuleContextDecl<CodeScope* scope> = (.Expression eHead, eGuards, eBody; .)
// "rule" "context" tagcolon MetaSimpExpr<eHead>
// "case" lparen MetaSimpExpr<eGuards> rparen
// lcurbrack MetaSimpExpr<eBody> rcurbrack (.scope->contextRules.push_back(Expression(Operator::CONTEXT_RULE, {eHead, eGuards, eBody})); .).
// Domain<DomainAnnotation& dom> =
// (
// "function" (. dom = DomainAnnotation::FUNCTION; .)
// | "variable" (. dom = DomainAnnotation::VARIABLE; .)
// ).
// RGuard<RuleGuards& guards>= (. Expression e; .)
// MetaExpr<e> (. guards.add(std::move(e)); .).
// MetaExpr<Expression& e>= (.Operator op; Expression e2; .)
// MetaExpr2<e>
// [MetaOp<op> MetaExpr2<e2> (. e = Expression(op, {e, e2}); .)
// ].
// MetaExpr2<Expression& e>=
// (
// lparen MetaExpr<e> rparen
// | MetaSimpExpr<e>
// ).
MetaSimpExpr<Expression& e>= (. std::wstring i1, infix; Expression e2; .)
(
'-' MetaSimpExpr<e2> (. e = Expression(Operator::NEG, {e2}); .)
| IF(checkParametersList()) Ident<i1>
(. e = Expression(Operator::CALL, {Expression(Atom<Identifier_t>(i1))});
if (!root->recognizeVariantConstructor(e))
SemErr({L"Undefined predicate: ", i1});
.)
lparen [ MetaCalleeParams<e> ] rparen
| IF(checkInfix()) Ident<i1> Ident<infix> MetaSimpExpr<e2>
(. e = Expression(Operator::CALL, {Expression(Atom<Identifier_t>(infix))});
e.addArg(Expression(Atom<Identifier_t>(i1)));
e.addArg(std::move(e2));
.)
| IdentR<e>
| number (. e = Expression(Atom<Number_t>(t->val)); .)
).
MetaCalleeParams<Expression& e> = (. Expression e2; .)
MetaSimpExpr<e2> (. e.addArg(Expression(e2)); .)
{comma MetaSimpExpr<e2> (. e.addArg(Expression(e2)); .)
}.
// RBody<const RuleArguments& args, const RuleGuards& guards> =
// (. Expression e; std::wstring msg; .)
// "warning" MetaExpr<e> ["message" string (. msg = t->val; .)
// ] (. root->add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom<String_t>(msg))); .)
// .
// MetaOp< Operator& op> =
// implic (. op = Operator::IMPL; .)
// .
/*============================ Expressions ===============================*/
ExprAnnotations<Expression& e> = (. TypeAnnotation typ; std::list<Expression> tags; Expression tag; e.tags.clear();.)
Type<typ> (. e.bindType(move(typ)); .)
{';' MetaSimpExpr<tag> (. tags.push_back(tag); .)
} (. e.addTags(tags); .)
.
ExprTyped<Expression&e> = Expr<e> [tagcolon ExprAnnotations<e>].
Expr< Expression& e> (. Expression e2; .)
= ExprLogicAnd<e>
[ ("or" | "OR") Expr<e2> (. e = Expression(Operator::OR, {e, e2}); .)
]
.
ExprLogicAnd< Expression& e> (. Expression e2; .)
= ExprRel<e>
[ ("and" | "AND") ExprLogicAnd <e2> (. e = Expression(Operator::AND, {e, e2}); .)
]
.
ExprRel< Expression& e> (. Operator op; Expression e2; .)
= ExprArithmAdd<e>
[ RelOp<op> ExprRel<e2> (. e = Expression(op, {e, e2}); .)
]
.
ExprArithmAdd< Expression& e>= (. Operator op; Expression e2; .)
ExprArithmMul< e>
[
AddOp<op>ExprArithmAdd< e2> (. e = Expression(op, {e, e2});.)
].
ExprArithmMul< Expression& e> (. Operator op; Expression e2; .)
= ExprUpdate<e>
[ MulOp< op>
ExprArithmMul< e2> (. e = Expression(op, {e, e2}); .)
].
ExprUpdate<Expression& e>= (. Expression e2; .)
ExprPostfix< e>
[
colon
( IF(checkListIndex()) ListIndexLiteral<e2>
| ListLiteral<e2>) (. e = Expression(Operator::UPDATE, {e, e2}); .)
].
ExprPostfix<Expression& e>
= Term<e>
[ (. e = Expression(Operator::INDEX, {e}); .)
{lbrack CalleeParams<e> rbrack }
].
Term< Expression& e> (. std::wstring name; e = Expression(); .)
=
(IF (checkParametersList()) Ident< name>
(. e = Expression(Operator::CALL, {Atom<Identifier_t>(name)}); root->recognizeVariantConstructor(e); .)
lparen [CalleeParams<e>] rparen
| IdentVR<e>
| ListLiteral<e>
| ListRangeLiteral<e>
| LoopDecl<e>
| IfDecl<e>
| SwitchDecl<e, SWITCH_NORMAL>
| IntrinsicDecl<e>
| SequenceDecl<e>
| number (. e = Expression(Atom<Number_t>(t->val)); .)
| string (. e = Expression(Atom<String_t>(t->val)); .)
| "true" (. e = Expression(Atom<Number_t>(1)); e.bindType(TypePrimitive::Bool); .)
| "false" (. e = Expression(Atom<Number_t>(0)); e.bindType(TypePrimitive::Bool); .)
| "undef" (. e = Expression(Operator::UNDEF, {}); .)
| '-' Term<e> (. e = Expression(Operator::NEG, {e}); .)
| lparen ExprTyped<e> rparen
).
ListLiteral<Expression& e> = (. std::wstring key;
Expression val;
std::list<Atom<Identifier_t>> keys;
e = Expression(Operator::LIST, {});
.)
lcurbrack {
( IF(checkTokenAfterIdent(_assign)) Ident<key> assign
| (. key = L""; .)
) Expr<val> (. keys.push_back(Atom<Identifier_t>(key));
e.operands.push_back(val);
.)
[comma] } rcurbrack (. e.addBindings(keys.begin(), keys.end()); .)
.
ListIndexLiteral<Expression& e> = (. e = Expression(Operator::LIST_INDEX, {});Expression valE;.)
lcurbrack
{ (. Expression idxE(Operator::LIST, {});.)
lbrack CalleeParams<idxE> rbrack
assign Expr<valE>[comma] (. e.operands.push_back(idxE); e.operands.push_back(valE); .)
}
rcurbrack
.
ListRangeLiteral<Expression& e> = (. Expression eFrom, eTo; .)
lbrack Expr<eFrom> ".." Expr<eTo> rbrack (. e = Expression(Operator::LIST_RANGE, {eFrom, eTo}); .)
.
CalleeParams<Expression& e> = (. Expression e2; .)
ExprTyped<e2> (. e.addArg(Expression(e2)); .)
{comma ExprTyped<e2> (. e.addArg(Expression(e2)); .)
}.
AddOp< Operator& op>
= (. op = Operator::ADD; .)
( '+'
| '-' (. op = Operator::SUB; .)
).
MulOp< Operator& op>
= (. op = Operator::MUL; .)
( '*'
| '/' (. op = Operator::DIV; .)
| '%' (. op = Operator::MOD; .)
).
RelOp< Operator& op>
= (. op = Operator::EQU; .)
( equal
| (ne1 | ne2) (. op = Operator::NE; .)
| lse (. op = Operator::LSE; .)
| lss (. op = Operator::LSS; .)
| gte (. op = Operator::GTE; .)
| gtr (. op = Operator::GTR; .)
).
SkipModulesSection = "module" {ANY} (lcurbrack {ANY} rcurbrack | '.').
END Xreate.
diff --git a/scripts/compilation/llvmaliases.xreate b/scripts/compilation/llvmaliases.xreate
new file mode 100644
index 0000000..e02cbaf
--- /dev/null
+++ b/scripts/compilation/llvmaliases.xreate
@@ -0,0 +1,5 @@
+Rec = type {x:: int, y:: int}.
+
+test = function(a:: int, b::int):: Rec; entry() {
+ {x = a + b, y = 2}
+}

Event Timeline