Introduce the new polymorphic approach to expressions

This commit is contained in:
Sameer Rahmani 2021-04-05 19:02:30 +01:00
parent 062b24e701
commit f73cfe7aaf
12 changed files with 333 additions and 114 deletions

View File

@ -22,7 +22,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "serene/serene.hpp" #include "serene/serene.h"
#include "serene/reader/reader.hpp" #include "serene/reader/reader.hpp"
#include "serene/sir/sir.hpp" #include "serene/sir/sir.hpp"
#include <iostream> #include <iostream>

View File

@ -0,0 +1,69 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef EXPRS_EXPRESSION_H
#define EXPRS_EXPRESSION_H
#include <memory>
namespace serene {
/// Contains all the builtin expressions including those which do not appear in
/// the syntax directly. Like function definitions.
namespace exprs {
/// The polymorphic type that works as the entry point to the exprs system.
/// Each expression has to define the interface of the `ExpressionConcept`
/// class as generic functions. **REMEMBER TO NOT INHERIT FROM THESE CLASSES**
class Expression {
public:
template <typename T>
Expression(T e) : self(new ExpressionImpl<T>(std::move(e))){};
private:
/// The generic interface which each type of expression has to implement
/// in order to act like an `Expression`
struct ExpressionConcept {
virtual ~ExpressionConcept() = default;
virtual ExpressionConcept *copy_() const = 0;
};
/// The generic implementation of `ExpressionConcept` which acts as the
/// dispatcher on type.
template <typename T> struct ExpressionImpl : ExpressionConcept {
ExpressionImpl(T e) : expr(std::move(e)){};
ExpressionConcept *copy_() const { return new ExpressionImpl(*this); }
T expr;
};
/// The internal container to keep the object implementing the
/// `ExpressionConcept`. This might be a `List` for example or a `Symbol`.
std::unique_ptr<ExpressionConcept> self;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -0,0 +1,43 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef EXPRS_SYMBOL_H
#define EXPRS_SYMBOL_H
#include "serene/exprs/expression.h"
#include "llvm/ADT/SmallVector.h"
#include <string>
namespace serene {
namespace exprs {
struct List {
llvm::SmallVector<Expression, 0> elements;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -0,0 +1,41 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef EXPRS_SYMBOL_H
#define EXPRS_SYMBOL_H
#include <string>
namespace serene {
namespace exprs {
struct Symbol {
std::string name;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -37,7 +37,7 @@
#include "serene/list.hpp" #include "serene/list.hpp"
#include "serene/logger.hpp" #include "serene/logger.hpp"
#include "serene/reader/location.hpp" #include "serene/reader/location.hpp"
#include "serene/serene.hpp" #include "serene/serene.h"
#include "serene/symbol.hpp" #include "serene/symbol.hpp"
#if defined(ENABLE_READER_LOG) || defined(ENABLE_LOG) #if defined(ENABLE_READER_LOG) || defined(ENABLE_LOG)

View File

@ -1,7 +1,7 @@
/** /*
* Serene programming language. * Serene programming language.
* *
* Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org> * Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal

View File

@ -55,8 +55,8 @@ private:
// TODO: Should we use builder here? maybe there is a better option // TODO: Should we use builder here? maybe there is a better option
::mlir::Location toMLIRLocation(serene::reader::Location *); ::mlir::Location toMLIRLocation(serene::reader::Location *);
mlir::FuncOp generateFn(serene::reader::Location, std::string, List *, // mlir::FuncOp generateFn(serene::reader::Location, std::string, List *,
List *); // List *);
public: public:
Generator(mlir::MLIRContext &context, ::serene::Namespace *ns) Generator(mlir::MLIRContext &context, ::serene::Namespace *ns)

View File

@ -1,6 +1,14 @@
set(HEADER_LIST set(HEADER_LIST
"${INCLUDE_DIR}/serene/serene.h"
"${INCLUDE_DIR}/serene/exprs/expression.h"
"${INCLUDE_DIR}/serene/exprs/symbol.h"
"${INCLUDE_DIR}/serene/exprs/list.h"
"${INCLUDE_DIR}/serene/expr.hpp" "${INCLUDE_DIR}/serene/expr.hpp"
"${INCLUDE_DIR}/serene/serene.hpp"
"${INCLUDE_DIR}/serene/number.hpp" "${INCLUDE_DIR}/serene/number.hpp"
"${INCLUDE_DIR}/serene/symbol.hpp" "${INCLUDE_DIR}/serene/symbol.hpp"
"${INCLUDE_DIR}/serene/list.hpp" "${INCLUDE_DIR}/serene/list.hpp"
@ -19,6 +27,9 @@ set(HEADER_LIST
# Make an automatic library - will be static or dynamic based on user setting # Make an automatic library - will be static or dynamic based on user setting
add_library(serene add_library(serene
exprs/symbol.cpp
exprs/list.cpp
serene.cpp serene.cpp
symbol.cpp symbol.cpp
list.cpp list.cpp

25
src/exprs/list.cpp Normal file
View File

@ -0,0 +1,25 @@
/*
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "serene/exprs/list.h"

25
src/exprs/symbol.cpp Normal file
View File

@ -0,0 +1,25 @@
/*
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "serene/exprs/symbol.h"

View File

@ -22,6 +22,6 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "serene/serene.hpp" #include "serene/serene.h"
#include "serene/reader/reader.hpp" #include "serene/reader/reader.hpp"
#include <iostream> #include <iostream>

View File

@ -40,139 +40,144 @@ namespace serene {
namespace sir { namespace sir {
mlir::ModuleOp Generator::generate() { mlir::ModuleOp Generator::generate() {
for (auto x : ns->Tree()) { // for (auto x : ns->Tree()) {
generate(x.get()); // generate(x.get());
} // }
return module; return module;
}; };
mlir::Operation *Generator::generate(AExpr *x) { mlir::Operation *Generator::generate(AExpr *x) {
switch (x->getType()) { // switch (x->getType()) {
case SereneType::Number: { // case SereneType::Number: {
return generate(llvm::cast<Number>(x)); // return generate(llvm::cast<Number>(x));
} // }
case SereneType::List: { // case SereneType::List: {
generate(llvm::cast<List>(x)); // generate(llvm::cast<List>(x));
return nullptr; // return nullptr;
} // }
default: { // default: {
return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)3); return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)3);
} // }
} // }
}; };
mlir::Value Generator::generate(List *l) { mlir::Value Generator::generate(List *l) {
auto first = l->at(0); // auto first = l->at(0);
if (!first) { // if (!first) {
// Empty list. // // Empty list.
// TODO: Return Nil or empty list. // // TODO: Return Nil or empty list.
// Just for now. // // Just for now.
return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)0); // return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)0);
}
if (first->get()->getType() == SereneType::Symbol) {
auto fnNameSymbol = llvm::dyn_cast<Symbol>(first->get());
if (fnNameSymbol->getName() == "fn") {
if (l->count() <= 3) {
module.emitError("'fn' form needs exactly 2 arguments.");
}
auto args = llvm::dyn_cast<List>(l->at(1).getValue().get());
auto body = llvm::dyn_cast<List>(l->from(2).get());
if (!args) {
module.emitError("The first element of 'def' has to be a symbol.");
}
// Create a new anonymous function and push it to the anonymous functions
// map, later on we
auto loc(fnNameSymbol->location->start);
auto anonymousName = fmt::format("__fn_{}__", anonymousFnCounter);
anonymousFnCounter++;
auto fn = generateFn(loc, anonymousName, args, body);
mlir::Identifier fnid = builder.getIdentifier(anonymousName);
anonymousFunctions.insert({fnid, fn});
return builder.create<FnIdOp>(builder.getUnknownLoc(), fnid.str());
}
}
// auto rest = l->from(1);
// auto loc = toMLIRLocation(&first->get()->location->start);
// for (auto x : *rest) {
// generate(x.get());
// } // }
// if (first->get()->getType() == SereneType::Symbol) {
// auto fnNameSymbol = llvm::dyn_cast<Symbol>(first->get());
// if (fnNameSymbol->getName() == "fn") {
// if (l->count() <= 3) {
// module.emitError("'fn' form needs exactly 2 arguments.");
// }
// auto args = llvm::dyn_cast<List>(l->at(1).getValue().get());
// auto body = llvm::dyn_cast<List>(l->from(2).get());
// if (!args) {
// module.emitError("The first element of 'def' has to be a symbol.");
// }
// // Create a new anonymous function and push it to the anonymous
// functions
// // map, later on we
// auto loc(fnNameSymbol->location->start);
// auto anonymousName = fmt::format("__fn_{}__", anonymousFnCounter);
// anonymousFnCounter++;
// auto fn = generateFn(loc, anonymousName, args, body);
// mlir::Identifier fnid = builder.getIdentifier(anonymousName);
// anonymousFunctions.insert({fnid, fn});
// return builder.create<FnIdOp>(builder.getUnknownLoc(), fnid.str());
// }
// }
// // auto rest = l->from(1);
// // auto loc = toMLIRLocation(&first->get()->location->start);
// // for (auto x : *rest) {
// // generate(x.get());
// // }
return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)100); return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)100);
}; };
mlir::FuncOp Generator::generateFn(serene::reader::Location loc, // mlir::FuncOp Generator::generateFn(serene::reader::Location loc,
std::string name, List *args, List *body) { // std::string name, List *args, List *body)
// {
auto location = toMLIRLocation(&loc); // auto location = toMLIRLocation(&loc);
llvm::SmallVector<mlir::Type, 4> arg_types(args->count(), // llvm::SmallVector<mlir::Type, 4> arg_types(args->count(),
builder.getI64Type()); // builder.getI64Type());
auto func_type = builder.getFunctionType(arg_types, builder.getI64Type()); // auto func_type = builder.getFunctionType(arg_types, builder.getI64Type());
auto proto = mlir::FuncOp::create(location, name, func_type); // auto proto = mlir::FuncOp::create(location, name, func_type);
mlir::FuncOp fn(proto); // mlir::FuncOp fn(proto);
if (!fn) { // if (!fn) {
module.emitError("Can not create the function."); // module.emitError("Can not create the function.");
} // }
auto &entryBlock = *fn.addEntryBlock(); // auto &entryBlock = *fn.addEntryBlock();
llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value> scope(symbolTable); // llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value>
// scope(symbolTable);
// Declare all the function arguments in the symbol table. // // Declare all the function arguments in the symbol table.
for (const auto arg : // for (const auto arg :
llvm::zip(args->asArrayRef(), entryBlock.getArguments())) { // llvm::zip(args->asArrayRef(), entryBlock.getArguments())) {
auto argSymbol = llvm::dyn_cast<Symbol>(std::get<0>(arg).get()); // auto argSymbol = llvm::dyn_cast<Symbol>(std::get<0>(arg).get());
if (!argSymbol) { // if (!argSymbol) {
module.emitError("Function parameters must be symbols"); // module.emitError("Function parameters must be symbols");
} // }
if (symbolTable.count(argSymbol->getName())) { // if (symbolTable.count(argSymbol->getName())) {
return nullptr; // return nullptr;
} // }
symbolTable.insert(argSymbol->getName(), std::get<1>(arg)); // symbolTable.insert(argSymbol->getName(), std::get<1>(arg));
} // }
// Set the insertion point in the builder to the beginning of the function // // Set the insertion point in the builder to the beginning of the function
// body, it will be used throughout the codegen to create operations in this // // body, it will be used throughout the codegen to create operations in
// function. // this
builder.setInsertionPointToStart(&entryBlock); // // function.
// builder.setInsertionPointToStart(&entryBlock);
// Emit the body of the function. // // Emit the body of the function.
if (!generate(body)) { // if (!generate(body)) {
fn.erase(); // fn.erase();
return nullptr; // return nullptr;
} // }
// // Implicitly return void if no return statement was emitted. // // // Implicitly return void if no return statement was emitted.
// // FIXME: we may fix the parser instead to always return the last // // // FIXME: we may fix the parser instead to always return the last
// expression // // expression
// // (this would possibly help the REPL case later) // // // (this would possibly help the REPL case later)
// ReturnOp returnOp; // // ReturnOp returnOp;
// if (!entryBlock.empty()) // // if (!entryBlock.empty())
// returnOp = dyn_cast<ReturnOp>(entryBlock.back()); // // returnOp = dyn_cast<ReturnOp>(entryBlock.back());
// if (!returnOp) { // // if (!returnOp) {
// builder.create<ReturnOp>(loc(funcAST.getProto()->loc())); // // builder.create<ReturnOp>(loc(funcAST.getProto()->loc()));
// } else if (returnOp.hasOperand()) { // // } else if (returnOp.hasOperand()) {
// // Otherwise, if this return operation has an operand then add a result // // // Otherwise, if this return operation has an operand then add a
// to // result
// // the function. // // to
// function.setType(builder.getFunctionType(function.getType().getInputs(), // // // the function.
// getType(VarType{}))); // // function.setType(builder.getFunctionType(function.getType().getInputs(),
// } // // getType(VarType{})));
// // }
return fn; // return fn;
} // }
mlir::Operation *Generator::generate(Number *x) { mlir::Operation *Generator::generate(Number *x) {
return builder.create<ValueOp>(builder.getUnknownLoc(), x->toI64()); return builder.create<ValueOp>(builder.getUnknownLoc(), x->toI64());