From 573550ca07441fca208a5b3796f2a39ac87c0ade Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sat, 24 Apr 2021 14:39:43 +0100 Subject: [PATCH] Add Fn expression and List rewrite to Fn --- include/serene/errors/constants.h | 8 +++ include/serene/exprs/def.h | 1 - include/serene/exprs/expression.h | 10 +++- include/serene/exprs/fn.h | 67 +++++++++++++++++++++++++ src/serene/CMakeLists.txt | 2 + src/serene/exprs/expression.cpp | 14 +++--- src/serene/exprs/fn.cpp | 79 ++++++++++++++++++++++++++++++ src/serene/exprs/list.cpp | 23 ++------- src/tests/exprs/list_tests.cpp.inc | 66 ++++++++++++++++--------- 9 files changed, 222 insertions(+), 48 deletions(-) create mode 100644 include/serene/exprs/fn.h create mode 100644 src/serene/exprs/fn.cpp diff --git a/include/serene/errors/constants.h b/include/serene/errors/constants.h index bd6b8fa..86e79c0 100644 --- a/include/serene/errors/constants.h +++ b/include/serene/errors/constants.h @@ -35,6 +35,8 @@ enum ErrID { E0000, E0001, E0002, + E0003, + E0004, }; struct ErrorVariant { @@ -55,6 +57,12 @@ static ErrorVariant static ErrorVariant DefWrongNumberOfArgs( E0002, "Wrong number of arguments is passed to the 'def' form.", ""); +static ErrorVariant FnNoArgsList(E0003, "'fn' form requires an argument list.", + ""); + +static ErrorVariant FnArgsMustBeList(E0004, "'fn' arguments should be a list.", + ""); + static std::map ErrDesc = { {E0000, &UnknownError}, {E0001, &DefExpectSymbol}, diff --git a/include/serene/exprs/def.h b/include/serene/exprs/def.h index e7793cb..5e884b5 100644 --- a/include/serene/exprs/def.h +++ b/include/serene/exprs/def.h @@ -53,7 +53,6 @@ public: maybe_node analyze(reader::SemanticContext &); static bool classof(const Expression *e); - // static std::shared_ptr isValid(List *); static maybe_node make(List *); ~Def() = default; }; diff --git a/include/serene/exprs/expression.h b/include/serene/exprs/expression.h index 9b71ca4..c43118d 100644 --- a/include/serene/exprs/expression.h +++ b/include/serene/exprs/expression.h @@ -46,6 +46,12 @@ enum class ExprType { Number, Def, Error, + Fn, +}; + +/// The string represantion of built in expr types (NOT DATATYPES). +static const char *exprTypes[] = { + "Symbol", "List", "Number", "Def", "Error", "Fn", }; class Expression; @@ -115,7 +121,9 @@ std::shared_ptr makeAndCast(Args &&...args) { /// Convert the given AST to string by calling the `toString` method /// of each node. -std::string toString(ast &); +std::string astToString(const ast *); +/// Converts the given ExprType to string. +std::string stringifyExprType(ExprType); /// Converts the given AST to string and prints it out void dump(ast &); diff --git a/include/serene/exprs/fn.h b/include/serene/exprs/fn.h new file mode 100644 index 0000000..116e81d --- /dev/null +++ b/include/serene/exprs/fn.h @@ -0,0 +1,67 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 Sameer Rahmani + * + * 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_FN_H +#define EXPRS_FN_H + +#include "serene/errors/error.h" +#include "serene/exprs/expression.h" +#include "serene/exprs/list.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace serene { + +namespace exprs { +class List; + +/// This data structure represents a function. with a collection of +/// arguments and the ast of a body +class Fn : public Expression { + +public: + std::string name; + + // TODO: Use a coll type instead of a list here + List args; + ast body; + + Fn(reader::LocationRange &loc, List &args, ast body) + : Expression(loc), args(args), body(body){}; + + ExprType getType() const; + std::string toString() const; + maybe_node analyze(reader::SemanticContext &); + + static bool classof(const Expression *e); + static maybe_node make(List *); + ~Fn() = default; +}; + +} // namespace exprs +} // namespace serene + +#endif diff --git a/src/serene/CMakeLists.txt b/src/serene/CMakeLists.txt index eaea3a7..2f674cf 100644 --- a/src/serene/CMakeLists.txt +++ b/src/serene/CMakeLists.txt @@ -7,6 +7,7 @@ set(HEADER_LIST "${INCLUDE_DIR}/serene/exprs/list.h" "${INCLUDE_DIR}/serene/exprs/number.h" "${INCLUDE_DIR}/serene/exprs/def.h" + "${INCLUDE_DIR}/serene/exprs/fn.h" # Reader "${INCLUDE_DIR}/serene/reader/reader.h" @@ -32,6 +33,7 @@ add_library(serene exprs/number.cpp exprs/expression.cpp exprs/def.cpp + exprs/fn.cpp serene.cpp diff --git a/src/serene/exprs/expression.cpp b/src/serene/exprs/expression.cpp index 1f0d177..4904d44 100644 --- a/src/serene/exprs/expression.cpp +++ b/src/serene/exprs/expression.cpp @@ -28,22 +28,24 @@ namespace serene { namespace exprs { -std::string toString(ast &tree) { - if (tree.size() == 0) { +std::string astToString(const ast *tree) { + if (tree->size() == 0) { return ""; } - std::string result = tree.at(0)->toString(); + std::string result = tree->at(0)->toString(); - for (unsigned int i = 1; i < tree.size(); i++) { - result = llvm::formatv("{0} {1}", result, tree.at(i)->toString()); + for (unsigned int i = 1; i < tree->size(); i++) { + result = llvm::formatv("{0} {1}", result, tree->at(i)->toString()); } return result; } +std::string stringifyExprType(ExprType t) { return exprTypes[(int)t]; }; + /// Dump the given AST tree to the standard out -void dump(ast &tree) { llvm::outs() << toString(tree) << "\n"; }; +void dump(ast &tree) { llvm::outs() << astToString(&tree) << "\n"; }; } // namespace exprs } // namespace serene diff --git a/src/serene/exprs/fn.cpp b/src/serene/exprs/fn.cpp new file mode 100644 index 0000000..346099b --- /dev/null +++ b/src/serene/exprs/fn.cpp @@ -0,0 +1,79 @@ +/* + * Serene programming language. + * + * Copyright (c) 2019-2021 Sameer Rahmani + * + * 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/fn.h" +#include "serene/errors/error.h" +#include "serene/exprs/expression.h" +#include "serene/exprs/list.h" +#include "serene/exprs/symbol.h" +#include "llvm/Support/FormatVariadic.h" + +namespace serene { +namespace exprs { + +ExprType Fn::getType() const { return ExprType::Fn; }; + +std::string Fn::toString() const { + return llvm::formatv("", + this->name.empty() ? "Anonymous" : this->name, + this->args.toString(), + this->body.empty() ? "<>" : astToString(&this->body)); +} + +maybe_node Fn::analyze(reader::SemanticContext &ctx) { + return Result::success(nullptr); +}; + +bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; }; + +maybe_node Fn::make(List *list) { + // TODO: Add support for docstring as the 3rd argument (4th element) + if (list->count() < 2) { + std::string msg = + llvm::formatv("The argument list is mandatory.", list->count()); + return Result::success(makeAndCast( + &errors::FnNoArgsList, list->elements[0], msg)); + } + + List *args = llvm::dyn_cast(list->elements[1].get()); + + if (!args) { + std::string msg = + llvm::formatv("Arguments of a function has to be a list, got '{0}'", + stringifyExprType(list->elements[1]->getType())); + return Result::success(makeAndCast( + &errors::FnArgsMustBeList, list->elements[1], msg)); + } + + ast body; + + if (list->count() > 2) { + body = std::vector(list->begin() + 2, list->end()); + } + + node fn = exprs::make(list->location, *args, body); + return Result::success(fn); +}; +} // namespace exprs +} // namespace serene diff --git a/src/serene/exprs/list.cpp b/src/serene/exprs/list.cpp index 84b13b5..8bb1369 100644 --- a/src/serene/exprs/list.cpp +++ b/src/serene/exprs/list.cpp @@ -25,6 +25,7 @@ #include "serene/exprs/list.h" #include "serene/errors/error.h" #include "serene/exprs/def.h" +#include "serene/exprs/fn.h" #include "serene/exprs/symbol.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -43,6 +44,7 @@ List::List(const reader::LocationRange &loc, ast elems) : Expression(loc), elements(elems){}; ExprType List::getType() const { return ExprType::List; }; + std::string List::toString() const { std::string s{this->elements.empty() ? "-" : ""}; @@ -65,24 +67,9 @@ maybe_node List::analyze(reader::SemanticContext &ctx) { return Def::make(this); } - /* if (sym->name == "fn") { */ - /* auto maybeErr = Fn::isValid(this); */ - - /* if (maybeErr) { */ - /* // Not a valid `def` form */ - /* return Result::success(maybeErr); */ - /* } */ - - /* Symbol *binding = llvm::dyn_cast(elements[1].get()); */ - - /* if (!binding) { */ - /* llvm_unreachable("Def::isValid should of catch this."); */ - /* } */ - - /* node def = make(location, binding->name, elements[2]); */ - /* return Result::success(def); */ - - /* } */ + if (sym->name == "fn") { + return Fn::make(this); + } } } diff --git a/src/tests/exprs/list_tests.cpp.inc b/src/tests/exprs/list_tests.cpp.inc index 2ef639f..fc693fa 100644 --- a/src/tests/exprs/list_tests.cpp.inc +++ b/src/tests/exprs/list_tests.cpp.inc @@ -25,9 +25,9 @@ #include "../test_helpers.cpp.inc" #include "serene/exprs/expression.h" #include "serene/exprs/list.h" +#include "serene/exprs/symbol.h" #include "serene/reader/reader.h" #include "serene/reader/semantics.h" -#include "serene/exprs/symbol.h" #include namespace serene { @@ -54,7 +54,8 @@ TEST_CASE("List Expression", "[expression]") { auto list3 = make(*range.get(), elements); - CHECK(list3->toString() == " > >"); + CHECK(list3->toString() == + " > >"); auto l = llvm::dyn_cast(list.get()); @@ -73,34 +74,55 @@ TEST_CASE("List Expression", "[expression]") { expr = l->at(2); REQUIRE_FALSE(expr.hasValue()); - for(auto x : *l) { + for (auto x : *l) { CHECK(x->getType() == ExprType::Symbol); } }; +TEST_CASE("List semantic analysis of 'def'", "[semantic]") { + reader::Semantics analyzer; + auto ast = reader::read("(def (a) b)"); + auto afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == ""); - TEST_CASE("List semantic analysis", "[semantic]") { - reader::Semantics analyzer; - auto ast = reader::read("(def (a) b)"); - auto afterAst = analyzer.analyze(ast.getValue()); - REQUIRE(afterAst); - CHECK(toString(afterAst.getValue()) == ""); + ast = reader::read("(def a)"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == ""); - ast = reader::read("(def a)"); - afterAst = analyzer.analyze(ast.getValue()); - REQUIRE(afterAst); - CHECK(toString(afterAst.getValue()) == ""); + ast = reader::read("(def a b c)"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == ""); - ast = reader::read("(def a b c)"); - afterAst = analyzer.analyze(ast.getValue()); - REQUIRE(afterAst); - CHECK(toString(afterAst.getValue()) == ""); + ast = reader::read("(def a b)"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == " >"); +} - ast = reader::read("(def a b)"); - afterAst = analyzer.analyze(ast.getValue()); - REQUIRE(afterAst); - CHECK(toString(afterAst.getValue()) == " >"); +TEST_CASE("List semantic analysis for 'fn'", "[semantic]") { + reader::Semantics analyzer; + auto ast = reader::read("(fn)"); + auto afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == ""); - } + ast = reader::read("(fn ())"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == " to <>>"); + + ast = reader::read("(fn (a b c) a a a)"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == " to >"); + + ast = reader::read("(fn () a b)"); + afterAst = analyzer.analyze(ast.getValue()); + REQUIRE(afterAst); + CHECK(astToString(&afterAst.getValue()) == " to >"); +} } // namespace exprs } // namespace serene