Browse Source

Everywhere: Add docstring support

Riyyi 4 months ago
parent
commit
536e55e75a
  1. 10
      lisp/load.bl
  2. 2
      lisp/other.bl
  3. 5
      lisp/predicate.bl
  4. 4
      src/ast.cpp
  5. 10
      src/ast.h
  6. 15
      src/env/environment.cpp
  7. 12
      src/env/environment.h
  8. 14
      src/env/functions/collection-access.cpp
  9. 10
      src/env/functions/collection-constructor.cpp
  10. 18
      src/env/functions/collection-modify.cpp
  11. 10
      src/env/functions/compare.cpp
  12. 14
      src/env/functions/convert.cpp
  13. 8
      src/env/functions/format.cpp
  14. 4
      src/env/functions/meta.cpp
  15. 8
      src/env/functions/mutable.cpp
  16. 13
      src/env/functions/operators.cpp
  17. 6
      src/env/functions/other.cpp
  18. 32
      src/env/functions/predicate.cpp
  19. 8
      src/env/functions/repl.cpp
  20. 10
      src/env/macro.h
  21. 203
      src/eval-special-form.cpp
  22. 10
      src/eval.cpp
  23. 15
      src/eval.h
  24. 22
      src/macro.h

10
lisp/load.bl

@ -1,9 +1,11 @@
(defn load-file [filename]
(eval (read-string (str "(do " (slurp filename) "\nnil)"))))
(defn load-file [file]
"Load the Lisp file named FILE."
(eval (read-string (str "(do " (slurp file) "\nnil)"))))
(defn load [filename]
(eval (read-string (str "(let* [] (do " (slurp filename) "))"))))
(defn load [file]
"Load the Lisp file named FILE."
(eval (read-string (str "(let* [] (do " (slurp file) "))"))))
;; Local Variables:
;; eval: (emacs-lisp-mode)

2
lisp/other.bl

@ -1,5 +1,5 @@
(def! *host-language* "C++")
(def *host-language* "C++")
;; Local Variables:
;; eval: (emacs-lisp-mode)

5
lisp/predicate.bl

@ -1,6 +1,7 @@
(defn not [cond]
(if cond false true))
(defn not [object]
"Return true if OBJECT is nil or false, and return false otherwise."
(if object false true))
;; Local Variables:
;; eval: (emacs-lisp-mode)

4
src/ast.cpp

@ -225,8 +225,10 @@ Callable::Callable(ValuePtr meta)
// -----------------------------------------
Function::Function(const std::string& name, FunctionType function)
Function::Function(std::string_view name, std::string_view signature, std::string_view documentation, FunctionType function)
: m_name(name)
, m_signature(signature)
, m_documentation(documentation)
, m_function(function)
{
}

10
src/ast.h

@ -332,11 +332,13 @@ using FunctionType = std::function<ValuePtr(ValueVectorConstIt, ValueVectorConst
class Function final : public Callable {
public:
Function(const std::string& name, FunctionType function);
Function(std::string_view name, std::string_view signature, std::string_view documentation, FunctionType function);
Function(const Function& that, ValuePtr meta);
virtual ~Function() = default;
const std::string& name() const { return m_name; }
std::string_view name() const { return m_name; }
std::string_view signature() const { return m_signature; }
std::string_view documentation() const { return m_documentation; }
FunctionType function() const { return m_function; }
WITH_META(Function);
@ -344,7 +346,9 @@ public:
private:
virtual bool isFunction() const override { return true; }
const std::string m_name;
std::string_view m_name;
std::string_view m_signature;
std::string_view m_documentation;
const FunctionType m_function;
};

15
src/env/environment.cpp vendored

@ -18,7 +18,7 @@
namespace blaze {
std::unordered_map<std::string, FunctionType> Environment::s_functions;
std::vector<FunctionParts> Environment::s_function_parts;
std::vector<std::string> Environment::s_lambdas;
EnvironmentPtr Environment::create()
@ -118,15 +118,20 @@ void Environment::loadFunctions()
}
}
void Environment::registerFunction(const std::string& name, FunctionType function)
void Environment::registerFunction(FunctionParts function_parts)
{
s_functions.insert_or_assign(name, function);
s_function_parts.push_back(function_parts);
}
void Environment::installFunctions(EnvironmentPtr env)
{
for (const auto& [name, function] : s_functions) {
env->set(name, makePtr<Function>(name, function));
for (const auto& function_parts : s_function_parts) {
env->set(std::string(function_parts.name),
makePtr<Function>(
function_parts.name,
function_parts.signature,
function_parts.documentation,
function_parts.function));
}
for (const auto& lambda : s_lambdas) {
// Ensure all s-exprs are run with (do)

12
src/env/environment.h vendored

@ -16,6 +16,14 @@
namespace blaze {
// All of these combined become a Function in the Environment
struct FunctionParts {
std::string_view name;
std::string_view signature;
std::string_view documentation;
FunctionType function;
};
class Environment {
public:
virtual ~Environment() = default;
@ -26,7 +34,7 @@ public:
static EnvironmentPtr create(const ValuePtr lambda, ValueVector&& arguments);
static void loadFunctions();
static void registerFunction(const std::string& name, FunctionType function);
static void registerFunction(FunctionParts function_parts);
static void installFunctions(EnvironmentPtr env);
bool exists(const std::string& symbol);
@ -53,7 +61,7 @@ private:
EnvironmentPtr m_outer { nullptr };
std::unordered_map<std::string, ValuePtr> m_values;
static std::unordered_map<std::string, FunctionType> s_functions;
static std::vector<FunctionParts> s_function_parts;
static std::vector<std::string> s_lambdas;
};

14
src/env/functions/collection-access.cpp vendored

@ -21,6 +21,8 @@ void Environment::loadCollectionAccess()
// (count {:foo 2 :bar 3}) -> 2
ADD_FUNCTION(
"count",
"",
"",
{
CHECK_ARG_COUNT_IS("count", SIZE(), 1);
@ -48,6 +50,8 @@ void Environment::loadCollectionAccess()
// (first (list 1 2 3)) -> 1
ADD_FUNCTION(
"first",
"",
"",
{
CHECK_ARG_COUNT_IS("first", SIZE(), 1);
@ -64,6 +68,8 @@ void Environment::loadCollectionAccess()
// (nth (list 1 2 3) 0) -> 1
ADD_FUNCTION(
"nth",
"",
"",
{
CHECK_ARG_COUNT_IS("nth", SIZE(), 2);
@ -83,6 +89,8 @@ void Environment::loadCollectionAccess()
// (rest (list 1 2 3)) -> (2 3)
ADD_FUNCTION(
"rest",
"",
"",
{
CHECK_ARG_COUNT_IS("rest", SIZE(), 1);
@ -101,6 +109,8 @@ void Environment::loadCollectionAccess()
// (get {:kw "value"} :kw) -> "value"
ADD_FUNCTION(
"get",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("get", SIZE(), 1);
@ -123,6 +133,8 @@ void Environment::loadCollectionAccess()
// (keys {"foo" 3 :bar 5}) -> ("foo" :bar)
ADD_FUNCTION(
"keys",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("keys", SIZE(), 1);
@ -149,6 +161,8 @@ void Environment::loadCollectionAccess()
// (vals {"foo" 3 :bar 5}) -> (3 5)
ADD_FUNCTION(
"vals",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("vals", SIZE(), 1);

10
src/env/functions/collection-constructor.cpp vendored

@ -19,6 +19,8 @@ void Environment::loadCollectionConstructor()
// (list 1 2) -> (1 2)
ADD_FUNCTION(
"list",
"",
"",
{
return makePtr<List>(begin, end);
});
@ -26,6 +28,8 @@ void Environment::loadCollectionConstructor()
// (make-list 4 nil) -> (nil nil nil nil)
ADD_FUNCTION(
"make-list",
"",
"",
{
CHECK_ARG_COUNT_IS("make-list", SIZE(), 2);
@ -80,6 +84,8 @@ void Environment::loadCollectionConstructor()
// (vec (list 1 2 3))
ADD_FUNCTION(
"vec",
"",
"",
{
CHECK_ARG_COUNT_IS("vec", SIZE(), 1);
@ -95,6 +101,8 @@ void Environment::loadCollectionConstructor()
// (vector 1 2 3) -> [1 2 3]
ADD_FUNCTION(
"vector",
"",
"",
{
auto result = makePtr<Vector>();
@ -106,6 +114,8 @@ void Environment::loadCollectionConstructor()
// (hash-map "foo" 5 :bar 10) -> {"foo" 5 :bar 10}
ADD_FUNCTION(
"hash-map",
"",
"",
{
CHECK_ARG_COUNT_EVEN("hash-map", SIZE());

18
src/env/functions/collection-modify.cpp vendored

@ -21,6 +21,8 @@ void Environment::loadCollectionModify()
// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) -> 10
ADD_FUNCTION(
"apply",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("apply", SIZE(), 2);
@ -54,6 +56,8 @@ void Environment::loadCollectionModify()
// (cons 1 (list 2 3))
ADD_FUNCTION(
"cons",
"",
"",
{
CHECK_ARG_COUNT_IS("cons", SIZE(), 2);
@ -73,6 +77,8 @@ void Environment::loadCollectionModify()
// (concat (list 1) (list 2 3)) -> (1 2 3)
ADD_FUNCTION(
"concat",
"",
"",
{
size_t count = 0;
for (auto it = begin; it != end; ++it) {
@ -95,6 +101,8 @@ void Environment::loadCollectionModify()
// (conj [1 2 3] 4 5 6) -> [1 2 3 4 5 6]
ADD_FUNCTION(
"conj",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("conj", SIZE(), 1);
@ -123,6 +131,8 @@ void Environment::loadCollectionModify()
// (map (fn* (x) (* x 2)) (list 1 2 3)) -> (2 4 6)
ADD_FUNCTION(
"map",
"",
"",
{
CHECK_ARG_COUNT_IS("map", SIZE(), 2);
@ -152,6 +162,8 @@ void Environment::loadCollectionModify()
// (set-nth (list 1 2 3) 1 "foo") -> (1 "foo" 3)
ADD_FUNCTION(
"set-nth",
"",
"",
{
CHECK_ARG_COUNT_IS("set-nth-element", SIZE(), 3);
@ -180,6 +192,8 @@ void Environment::loadCollectionModify()
// (seq "foo") -> ("f" "o" "o")
ADD_FUNCTION(
"seq",
"",
"",
{
CHECK_ARG_COUNT_IS("seq", SIZE(), 1);
@ -230,6 +244,8 @@ void Environment::loadCollectionModify()
// (assoc {:a 1 :b 2} :a 3 :c 1) -> {:a 3 :b 2 :c 1}
ADD_FUNCTION(
"assoc",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("assoc", SIZE(), 1);
@ -250,6 +266,8 @@ void Environment::loadCollectionModify()
// (dissoc {:a 1 :b 2 :c 3} :a :c :d) -> {:b 2}
ADD_FUNCTION(
"dissoc",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("dissoc", SIZE(), 1);

10
src/env/functions/compare.cpp vendored

@ -40,10 +40,10 @@ void Environment::loadCompare()
return makePtr<Constant>((result) ? Constant::True : Constant::False); \
}
ADD_FUNCTION("<", NUMBER_COMPARE(<));
ADD_FUNCTION("<=", NUMBER_COMPARE(<=));
ADD_FUNCTION(">", NUMBER_COMPARE(>));
ADD_FUNCTION(">=", NUMBER_COMPARE(>=));
ADD_FUNCTION("<", "", "", NUMBER_COMPARE(<));
ADD_FUNCTION("<=", "", "", NUMBER_COMPARE(<=));
ADD_FUNCTION(">", "", "", NUMBER_COMPARE(>));
ADD_FUNCTION(">=", "", "", NUMBER_COMPARE(>=));
// -----------------------------------------
@ -51,6 +51,8 @@ void Environment::loadCompare()
// (= "foo" "foo") -> true
ADD_FUNCTION(
"=",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("=", SIZE(), 2);

14
src/env/functions/convert.cpp vendored

@ -18,6 +18,8 @@ void Environment::loadConvert()
// (number-to-string 123) -> "123"
ADD_FUNCTION(
"number-to-string",
"",
"",
{
CHECK_ARG_COUNT_IS("number-to-string", SIZE(), 1);
@ -37,6 +39,8 @@ void Environment::loadConvert()
// (string-to-char "123") -> 49
ADD_FUNCTION(
"string-to-char",
"",
"",
{
CHECK_ARG_COUNT_IS("string-to-char", SIZE(), 1);
@ -49,6 +53,8 @@ void Environment::loadConvert()
// (string-to-number "123") -> 123
ADD_FUNCTION(
"string-to-number",
"",
"",
{
CHECK_ARG_COUNT_IS("string-to-number", SIZE(), 1);
@ -83,14 +89,16 @@ void Environment::loadConvert()
// (string-to-list "foo") -> (102 111 111)
// (string-to-vector "foo") -> [102 111 111]
ADD_FUNCTION("string-to-list", STRING_TO_COLLECTION("string-to-list", List));
ADD_FUNCTION("string-to-vector", STRING_TO_COLLECTION("string-to-vector", Vector));
ADD_FUNCTION("string-to-list", "", "", STRING_TO_COLLECTION("string-to-list", List));
ADD_FUNCTION("string-to-vector", "", "", STRING_TO_COLLECTION("string-to-vector", Vector));
// -------------------------------------
// (symbol "foo") -> foo
ADD_FUNCTION(
"symbol",
"",
"",
{
CHECK_ARG_COUNT_IS("symbol", SIZE(), 1);
@ -107,6 +115,8 @@ void Environment::loadConvert()
// (keyword 123) -> :123
ADD_FUNCTION(
"keyword",
"",
"",
{
CHECK_ARG_COUNT_IS("keyword", SIZE(), 1);

8
src/env/functions/format.cpp vendored

@ -34,8 +34,8 @@ void Environment::loadFormat()
return makePtr<String>(result); \
}
ADD_FUNCTION("str", PRINTER_STRING(false, ""));
ADD_FUNCTION("pr-str", PRINTER_STRING(true, " "));
ADD_FUNCTION("str", "", "", PRINTER_STRING(false, ""));
ADD_FUNCTION("pr-str", "", "", PRINTER_STRING(true, " "));
#define PRINTER_PRINT(print_readably) \
{ \
@ -52,8 +52,8 @@ void Environment::loadFormat()
return makePtr<Constant>(); \
}
ADD_FUNCTION("prn", PRINTER_PRINT(true));
ADD_FUNCTION("println", PRINTER_PRINT(false));
ADD_FUNCTION("prn", "", "", PRINTER_PRINT(true));
ADD_FUNCTION("println", "", "", PRINTER_PRINT(false));
}
} // namespace blaze

4
src/env/functions/meta.cpp vendored

@ -17,6 +17,8 @@ void Environment::loadMeta()
// (meta [1 2 3])
ADD_FUNCTION(
"meta",
"",
"",
{
CHECK_ARG_COUNT_IS("meta", SIZE(), 1);
@ -36,6 +38,8 @@ void Environment::loadMeta()
// (with-meta [1 2 3] "some metadata")
ADD_FUNCTION(
"with-meta",
"",
"",
{
CHECK_ARG_COUNT_IS("with-meta", SIZE(), 2);

8
src/env/functions/mutable.cpp vendored

@ -20,6 +20,8 @@ void Environment::loadMutable()
// (atom 1)
ADD_FUNCTION(
"atom",
"",
"",
{
CHECK_ARG_COUNT_IS("atom", SIZE(), 1);
@ -29,6 +31,8 @@ void Environment::loadMutable()
// (deref myatom)
ADD_FUNCTION(
"deref",
"",
"",
{
CHECK_ARG_COUNT_IS("deref", SIZE(), 1);
@ -40,6 +44,8 @@ void Environment::loadMutable()
// (reset! myatom 2)
ADD_FUNCTION(
"reset!",
"",
"",
{
CHECK_ARG_COUNT_IS("reset!", SIZE(), 2);
@ -54,6 +60,8 @@ void Environment::loadMutable()
// (swap! myatom (fn* [x y] (+ 1 x y)) 2) -> (deref (def! myatom (atom ((fn* [x y] (+ 1 x y)) (deref myatom) 2))))
ADD_FUNCTION(
"swap!",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("swap!", SIZE(), 2);

13
src/env/functions/operators.cpp vendored

@ -16,6 +16,8 @@ void Environment::loadOperators()
{
ADD_FUNCTION(
"+",
"number...",
"Return the sum of any amount of arguments, where NUMBER is of type number.",
{
int64_t result = 0;
@ -29,6 +31,11 @@ void Environment::loadOperators()
ADD_FUNCTION(
"-",
"[number] subtract...",
R"(Negate NUMBER or SUBTRACT numbers and return the result.
With one arg, negates it. With more than one arg,
subtracts all but the first from the first.)",
{
size_t length = SIZE();
if (length == 0) {
@ -54,6 +61,8 @@ void Environment::loadOperators()
ADD_FUNCTION(
"*",
"",
"",
{
int64_t result = 1;
@ -67,6 +76,8 @@ void Environment::loadOperators()
ADD_FUNCTION(
"/",
"",
"",
{
CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1);
@ -86,6 +97,8 @@ void Environment::loadOperators()
// (% 5 2) -> 1
ADD_FUNCTION(
"%",
"",
"",
{
CHECK_ARG_COUNT_IS("/", SIZE(), 2);

6
src/env/functions/other.cpp vendored

@ -5,9 +5,7 @@
*/
#include <chrono> // std::chrono::sytem_clock
#include <cstddef> // size_t
#include <cstdint> // int64_t
#include <memory> // std::static_pointer_cast
#include "ast.h"
#include "env/macro.h"
@ -22,6 +20,8 @@ void Environment::loadOther()
// (throw x)
ADD_FUNCTION(
"throw",
"",
"",
{
CHECK_ARG_COUNT_IS("throw", SIZE(), 1);
@ -35,6 +35,8 @@ void Environment::loadOther()
// (time-ms)
ADD_FUNCTION(
"time-ms",
"",
"",
{
CHECK_ARG_COUNT_IS("time-ms", SIZE(), 0);

32
src/env/functions/predicate.cpp vendored

@ -22,9 +22,9 @@ void Environment::loadPredicate()
}
// (nil? nil)
ADD_FUNCTION("nil?", IS_CONSTANT("nil?", Constant::Nil));
ADD_FUNCTION("true?", IS_CONSTANT("true?", Constant::True));
ADD_FUNCTION("false?", IS_CONSTANT("false?", Constant::False));
ADD_FUNCTION("nil?", "", "", IS_CONSTANT("nil?", Constant::Nil));
ADD_FUNCTION("true?", "", "", IS_CONSTANT("true?", Constant::True));
ADD_FUNCTION("false?", "", "", IS_CONSTANT("false?", Constant::False));
// -----------------------------------------
@ -47,18 +47,20 @@ void Environment::loadPredicate()
}
// (symbol? 'foo)
ADD_FUNCTION("atom?", IS_TYPE(Atom));
ADD_FUNCTION("keyword?", IS_TYPE(Keyword));
ADD_FUNCTION("list?", IS_TYPE(List));
ADD_FUNCTION("map?", IS_TYPE(HashMap));
ADD_FUNCTION("number?", IS_TYPE(Number));
ADD_FUNCTION("sequential?", IS_TYPE(Collection));
ADD_FUNCTION("string?", IS_TYPE(String));
ADD_FUNCTION("symbol?", IS_TYPE(Symbol));
ADD_FUNCTION("vector?", IS_TYPE(Vector));
ADD_FUNCTION("atom?", "", "", IS_TYPE(Atom));
ADD_FUNCTION("keyword?", "", "", IS_TYPE(Keyword));
ADD_FUNCTION("list?", "", "", IS_TYPE(List));
ADD_FUNCTION("map?", "", "", IS_TYPE(HashMap));
ADD_FUNCTION("number?", "", "", IS_TYPE(Number));
ADD_FUNCTION("sequential?", "", "", IS_TYPE(Collection));
ADD_FUNCTION("string?", "", "", IS_TYPE(String));
ADD_FUNCTION("symbol?", "", "", IS_TYPE(Symbol));
ADD_FUNCTION("vector?", "", "", IS_TYPE(Vector));
ADD_FUNCTION(
"fn?",
"",
"",
{
bool result = true;
@ -78,6 +80,8 @@ void Environment::loadPredicate()
ADD_FUNCTION(
"macro?",
"",
"",
{
bool result = true;
@ -101,6 +105,8 @@ void Environment::loadPredicate()
// (contains? {"bar" 5} "foo") -> false
ADD_FUNCTION(
"contains?",
"",
"",
{
CHECK_ARG_COUNT_IS("contains?", SIZE(), 2);
@ -117,6 +123,8 @@ void Environment::loadPredicate()
// (empty? [] [1 2 3] []) -> false
ADD_FUNCTION(
"empty?",
"",
"",
{
bool result = true;

8
src/env/functions/repl.cpp vendored

@ -19,6 +19,8 @@ void Environment::loadRepl()
// REPL reader
ADD_FUNCTION(
"read-string",
"",
"",
{
CHECK_ARG_COUNT_IS("read-string", SIZE(), 1);
@ -31,6 +33,8 @@ void Environment::loadRepl()
// Read file contents
ADD_FUNCTION(
"slurp",
"",
"",
{
CHECK_ARG_COUNT_IS("slurp", SIZE(), 1);
@ -45,6 +49,8 @@ void Environment::loadRepl()
// Prompt readline
ADD_FUNCTION(
"readline",
"",
"",
{
CHECK_ARG_COUNT_IS("readline", SIZE(), 1);
@ -56,6 +62,8 @@ void Environment::loadRepl()
// REPL eval
ADD_FUNCTION(
"eval",
"",
"",
{
CHECK_ARG_COUNT_IS("eval", SIZE(), 1);

10
src/env/macro.h vendored

@ -8,9 +8,11 @@
#include "env/environment.h"
#define ADD_FUNCTION(symbol, lambda) \
Environment::registerFunction( \
symbol, \
[](ValueVectorConstIt begin, ValueVectorConstIt end) -> blaze::ValuePtr lambda);
#define ADD_FUNCTION(name, signature, documentation, lambda) \
Environment::registerFunction( \
{ name, \
signature, \
documentation, \
[](ValueVectorConstIt begin, ValueVectorConstIt end) -> blaze::ValuePtr lambda });
#define SIZE() std::distance(begin, end)

203
src/eval-special-form.cpp

@ -4,17 +4,26 @@
* SPDX-License-Identifier: MIT
*/
#include <iterator> // std::distance, std::next, std::prev
#include <algorithm> // std::find_if, std::transform
#include <cctype> // std::toupper
#include <iterator> // std::distance, std::next, std::prev
#include <list>
#include <memory>
#include <span>
#include <string>
#include "ruc/format/color.h"
#include "ruc/format/format.h"
#include "ruc/format/print.h"
#include "ast.h"
#include "env/environment.h"
#include "error.h"
#include "eval.h"
#include "forward.h"
#include "macro.h"
#include "printer.h"
#include "settings.h"
#include "types.h"
#include "util.h"
@ -22,7 +31,7 @@ namespace blaze {
static ValuePtr evalQuasiQuoteImpl(ValuePtr ast);
// (def! x 2)
EVAL_FUNCTION("def!", "symbol value", "Set SYMBOL to the value VALUE.");
ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("def!", nodes.size(), 2);
@ -44,7 +53,13 @@ ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
return env->set(symbol->symbol(), value);
}
// (defmacro! x (fn* (x) x))
EVAL_FUNCTION("defmacro!", "symbol function",
R"(Define SYMBOL as a macro.
When the macro is called, as in (NAME ARGS...),
the FUNCTION (fn* ARGLIST BODY...) is applied to
the list ARGS... as it appears in the expression,
and the result should be a form to be evaluated instead of the original.)");
ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("defmacro!", nodes.size(), 2);
@ -67,7 +82,139 @@ ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
return env->set(symbol->symbol(), makePtr<Macro>(*lambda));
}
// (fn* (x) x)
EVAL_FUNCTION("describe", "symbol", "Display the full documentation of SYMBOL.");
ValuePtr Eval::evalDescribe(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("describe", nodes.size(), 1);
// First argument needs to be a Symbol
VALUE_CAST(symbol, Symbol, nodes.front());
auto symbol_string = symbol->symbol();
std::string type;
std::string signature;
std::string documentation;
std::string value_string;
bool pretty_print = Settings::the().get("pretty-print") == "1";
auto bold = fg(ruc::format::TerminalColor::None) | ruc::format::Emphasis::Bold;
auto describe = [&]() {
print("{} is a {}.\n\n", symbol_string, type);
if (!signature.empty()) {
pretty_print ? print(bold, "Signature\n") : print("Signature\n");
print("({})\n", signature);
}
if (!documentation.empty()) {
pretty_print ? print(bold, "\nDocumentation\n") : print("\nDocumentation\n");
print("{}\n", documentation);
}
if (!value_string.empty()) {
pretty_print ? print(bold, "Value\n") : print("Value\n");
print("{}\n", value_string);
}
};
// Verify if symbol is a special form
auto special_form = std::find_if(
s_special_form_parts.begin(),
s_special_form_parts.end(),
[&symbol_string](const SpecialFormParts& special_form_parts) {
return special_form_parts.name == symbol_string;
});
// If symbol is not special form, lookup in the environment
ValuePtr value;
if (special_form == s_special_form_parts.end()) {
value = env->get(symbol_string);
if (!value) {
Error::the().add(format("'{}' not found", symbol_string));
return nullptr;
}
}
// Variable
if (special_form == s_special_form_parts.end() && !is<Callable>(value.get())) {
type = "variable";
Printer printer;
value_string = format("{}", printer.printNoErrorCheck(value, true));
describe();
return nullptr;
}
signature = pretty_print ? format(fg(ruc::format::TerminalColor::BrightBlue), "{}", symbol_string)
: symbol_string;
// Special form
if (special_form != s_special_form_parts.end()) {
type = "special form";
std::string signature_lower = std::string(special_form->signature);
std::transform(signature_lower.begin(), signature_lower.end(), signature_lower.begin(), ::toupper);
signature += !signature_lower.empty() ? " " : "";
signature += signature_lower;
documentation = special_form->documentation;
describe();
return nullptr;
}
// Function / lambda / macro
if (is<Function>(value.get())) {
type = "function";
auto function = std::static_pointer_cast<Function>(value);
signature += !function->signature().empty() ? " " : "";
signature += function->signature();
documentation = function->documentation();
}
else if (is<Lambda>(value.get()) || is<Macro>(value.get())) {
type = is<Lambda>(value.get()) ? "function" : "macro";
auto lambda = std::static_pointer_cast<Lambda>(value);
auto bindings = lambda->bindings();
std::string binding;
for (size_t i = 0; i < bindings.size(); ++i) {
binding = bindings[i];
std::transform(binding.begin(), binding.end(), binding.begin(), ::toupper);
signature += " " + binding;
}
auto body = lambda->body();
if (is<String>(body.get())) {
documentation = std::static_pointer_cast<String>(body)->data();
}
else if (is<List>(body.get())) {
VALUE_CAST(list, List, body);
if (list->size() > 1) {
auto second = list->nodesRead()[1];
if (is<String>(second.get())) {
documentation = std::static_pointer_cast<String>(second)->data();
}
}
}
}
describe();
return nullptr;
}
EVAL_FUNCTION("fn*", "args [docstring] body...", R"(Return an anonymous function.
ARGS should take the form of an argument list or vector.
DOCSTRING is an optional documentation string.
If present, it should describe how to call the function.
BODY should be a list of Lisp expressions.)");
ValuePtr Eval::evalFn(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("fn*", nodes.size(), 2);
@ -102,6 +249,7 @@ ValuePtr Eval::evalFn(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (quasiquoteexpand x)
EVAL_FUNCTION("quasiquoteexpand", "arg", ""); // TODO
ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
{
CHECK_ARG_COUNT_IS("quasiquoteexpand", nodes.size(), 1);
@ -109,7 +257,7 @@ ValuePtr Eval::evalQuasiQuoteExpand(const ValueVector& nodes)
return evalQuasiQuoteImpl(nodes.front());
}
// (quote x)
EVAL_FUNCTION("quote", "arg", "Return the ARG, without evaluating it. (quote x) yields x.");
ValuePtr Eval::evalQuote(const ValueVector& nodes)
{
CHECK_ARG_COUNT_IS("quote", nodes.size(), 1);
@ -117,7 +265,13 @@ ValuePtr Eval::evalQuote(const ValueVector& nodes)
return nodes.front();
}
// (try* x ... (catch* y z))
EVAL_FUNCTION("try*", "body... [catch]", R"(Eval BODY allowing exceptions to get caught.
CATCH should take the form of (catch* binding handler).
The BODY is evaluated, if it throws an exception, then form CATCH is
handled by creating a new environment that binds the symbol BINDING
to the value of the exception that was thrown. Finally, HANDLER is evaluated.)");
ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1);
@ -169,11 +323,11 @@ ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
VALUE_CAST(catch_binding, Symbol, (*std::next(catch_nodes.begin())));
// Create new Environment that binds 'y' to the value of the exception
// Create new Environment that binds 'binding' to the value of the exception
auto catch_env = Environment::create(env);
catch_env->set(catch_binding->symbol(), error);
// Evaluate 'z' using the new Environment
// Evaluate 'handler' using the new Environment
m_ast = catch_nodes.back();
m_env = catch_env;
return evalImpl();
@ -181,7 +335,10 @@ ValuePtr Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (and 1 2 3)
EVAL_FUNCTION("and", "args...", R"(Eval ARGS until one of them yields nil, then return nil.
The remaining args are not evalled at all.
If no arg yields nil, return the last arg's value.)");
void Eval::evalAnd(const ValueVector& nodes, EnvironmentPtr env)
{
ValuePtr result = makePtr<Constant>(Constant::True);
@ -205,7 +362,7 @@ void Eval::evalAnd(const ValueVector& nodes, EnvironmentPtr env)
return; // TCO
}
// (do 1 2 3)
EVAL_FUNCTION("do", "body...", "Eval BODY forms sequentially and return value of the last one.");
void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("do", nodes.size(), 1, void());
@ -223,7 +380,11 @@ void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
return; // TCO
}
// (if x true false)
EVAL_FUNCTION("if", "COND THEN [ELSE]", R"(If COND yields non-nil, do THEN, else do ELSE.
Returns the value of THEN or the value of ELSE.
Both THEN and ELSE must be one expression.
If COND yields nil, and there is no ELSE, the value is nil.)");
void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_BETWEEN("if", nodes.size(), 2, 3, void());
@ -247,7 +408,12 @@ void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
return; // TCO
}
// (let* (x 1) x)
EVAL_FUNCTION("let*", "varlist body", R"(Bind variables accoring to VARLIST then eval BODY.
The value of the BODY form is returned.
VARLIST is a list or vector with an even amount of elements,
where each odd number is a symbol gets bind the even element.
All even elements are evalled before any symbols are bound.)");
void Eval::evalLet(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("let*", nodes.size(), 2, void());
@ -307,6 +473,7 @@ static bool isMacroCall(ValuePtr ast, EnvironmentPtr env)
return true;
}
EVAL_FUNCTION("macroexpand-1", "expression", "Macroexpand EXPRESSION and pretty-print its value.");
void Eval::evalMacroExpand1(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("macroexpand-1", nodes.size(), 1, void());
@ -329,7 +496,10 @@ void Eval::evalMacroExpand1(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (or 1 2 3)
EVAL_FUNCTION("or", "args...", R"(Eval ARGS until one of them yields non-nil, then return that value.
The remaining args are not evalled at all.
If all args return nil, return nil.)");
void Eval::evalOr(const ValueVector& nodes, EnvironmentPtr env)
{
ValuePtr result;
@ -442,6 +612,7 @@ static ValuePtr evalQuasiQuoteImpl(ValuePtr ast)
}
// (quasiquote x)
EVAL_FUNCTION("quasiquote", "arg", R"()"); // TODO
void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_IS("quasiquote", nodes.size(), 1, void());
@ -456,6 +627,12 @@ void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
// -----------------------------------------
// (while true body...)
EVAL_FUNCTION("while", "test body...", R"(If TEST yields non-nil, eval BODY... and repeat
The order of execution is thus TEST, BODY, TEST, BODY and so on
until TEST returns nil.
The value of a while form is always nil.)");
void Eval::evalWhile(const ValueVector& nodes, EnvironmentPtr env)
{
CHECK_ARG_COUNT_AT_LEAST("while", nodes.size(), 2, void());

10
src/eval.cpp

@ -20,6 +20,8 @@
namespace blaze {
std::vector<SpecialFormParts> Eval::s_special_form_parts;
Eval::Eval(ValuePtr ast, EnvironmentPtr env)
: m_ast(ast)
, m_env(env)
@ -29,6 +31,11 @@ Eval::Eval(ValuePtr ast, EnvironmentPtr env)
// -----------------------------------------
void Eval::registerSpecialForm(SpecialFormParts special_form_parts)
{
s_special_form_parts.push_back(special_form_parts);
}
void Eval::eval()
{
m_ast = evalImpl();
@ -80,6 +87,9 @@ ValuePtr Eval::evalImpl()
if (symbol == "defmacro!") {
return evalDefMacro(nodes, env);
}
if (symbol == "describe") {
return evalDescribe(nodes, env);
}
if (symbol == "fn*") {
return evalFn(nodes, env);
}

15
src/eval.h

@ -6,21 +6,27 @@
#pragma once
#include <list>
#include <stack>
#include <vector>
#include "env/environment.h"
#include "forward.h"
namespace blaze {
class List;
// All of these combined become a Function in the Environment
struct SpecialFormParts {
std::string_view name;
std::string_view signature;
std::string_view documentation;
};
class Eval {
public:
Eval(ValuePtr ast, EnvironmentPtr env);
virtual ~Eval() = default;
static void registerSpecialForm(SpecialFormParts special_form_parts);
void eval();
ValuePtr ast() const { return m_ast; }
@ -33,6 +39,7 @@ private:
ValuePtr evalDef(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalDefMacro(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalDescribe(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalFn(const ValueVector& nodes, EnvironmentPtr env);
ValuePtr evalQuasiQuoteExpand(const ValueVector& nodes);
ValuePtr evalQuote(const ValueVector& nodes);
@ -52,6 +59,8 @@ private:
ValuePtr m_ast;
EnvironmentPtr m_env;
EnvironmentPtr m_outer_env;
static std::vector<SpecialFormParts> s_special_form_parts;
};
} // namespace blaze

22
src/macro.h

@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyi
*
* SPDX-License-Identifier: MIT
*/
#include "eval.h"
#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define CONCAT_IMPL(a, b) a##b
#define EVAL_FUNCTION_IMPL(name, signature, documentation, struct_name) \
struct struct_name { \
struct_name() \
{ \
Eval::registerSpecialForm({ name, signature, documentation }); \
} \
}; \
static struct struct_name struct_name; // NOLINT(clang-diagnostic-unused-function)
#define EVAL_FUNCTION(name, signature, documentation) \
EVAL_FUNCTION_IMPL(name, signature, documentation, CONCAT(__EVAL_STRUCT_, __COUNTER__))
Loading…
Cancel
Save