diff --git a/src/builtin.cpp b/src/builtin.cpp index 104cecf..c4c55fa 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -90,14 +90,6 @@ void registerFunction2(std::unordered_map* variables, Allocator* } -bool isPair(Value obj) -{ - if (!obj.isPointer()) - return false; - return dynamic_cast(obj.asPointer()) != nullptr; -} - - } // namespace diff --git a/src/object.cpp b/src/object.cpp index 1760bf8..2bda17e 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -143,4 +143,12 @@ void ContinuationObject::mark() } +bool isPair(Value value) +{ + if (!value.isPointer()) + return false; + return dynamic_cast(value.asPointer()); +} + + } // namespace nscheme diff --git a/src/object.hpp b/src/object.hpp index 3649ed0..404b1ed 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -247,4 +247,8 @@ class ContinuationObject : public Object { }; +// utility functions +bool isPair(Value v); + + } // namespace nscheme diff --git a/src/parser.cpp b/src/parser.cpp index 01d8f67..1219fe3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -32,14 +32,6 @@ bool isSelfEvaluating(Value value) } -inline bool isPair(Value value) -{ - if (!value.isPointer()) - return false; - return dynamic_cast(value.asPointer()); -} - - template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); @@ -52,6 +44,22 @@ template std::unique_ptr make_unique(Args&&... namespace nscheme { +bool LocalNames::lookupSymbol(Symbol symbol, std::pair* out) const +{ + size_t frame_index = 0; + for (const LocalNames* p = this; p != nullptr; p = p->parent) { + auto it = p->name2index.find(symbol); + if (it != p->name2index.end()) { + out->first = frame_index; + out->second = it->second; + return true; + } + ++frame_index; + } + return false; +} + + std::unique_ptr Parser::parse(Value datum) { Position dummy(symbol_table_->intern(""), 1, 1); @@ -79,23 +87,6 @@ std::unique_ptr Parser::parse(Value datum) } -bool Parser::lookupSymbol(Symbol symbol, const LocalNames& names, - std::pair* out) const -{ - size_t frame_index = 0; - for (const LocalNames* p = &names; p != nullptr; p = p->parent) { - auto it = p->name2index.find(symbol); - if (it != p->name2index.end()) { - out->first = frame_index; - out->second = it->second; - return true; - } - ++frame_index; - } - return false; -} - - std::unique_ptr Parser::parseExprOrDefine(Value value, const Position& position, LocalNames& names) { @@ -146,7 +137,7 @@ std::unique_ptr Parser::parseVariable(Symbol symbol, const Position& p LocalNames& names) { std::pair index; - if (lookupSymbol(symbol, names, &index)) + if (names.lookupSymbol(symbol, &index)) return make_unique(position, index.first, index.second); else return make_unique(position, symbol); @@ -281,7 +272,7 @@ std::unique_ptr Parser::parseAssignment(Value value, const Position& p Symbol symbol = p1->getCar().asSymbol(); auto expr = parseExpr(p2->getCar(), source_map_->at(p2), names); std::pair index; - if (lookupSymbol(symbol, names, &index)) + if (names.lookupSymbol(symbol, &index)) return make_unique(position, index.first, index.second, std::move(expr)); else diff --git a/src/parser.hpp b/src/parser.hpp index 1f7fd2f..6bd2fa9 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -11,6 +11,19 @@ namespace nscheme { +struct LocalNames { + explicit LocalNames(LocalNames* parent) + : parent(parent) + { + } + + bool lookupSymbol(Symbol symbol, std::pair* out) const; + + LocalNames* parent; + std::unordered_map name2index; +}; + + class Parser { public: Parser(SymbolTable* symbol_table, SourceMap* source_map, @@ -29,16 +42,6 @@ class Parser { std::unique_ptr parse(Value datum); private: - struct LocalNames { - explicit LocalNames(LocalNames* parent) - : parent(parent) - { - } - LocalNames* parent; - std::unordered_map name2index; - }; - - bool lookupSymbol(Symbol symbol, const LocalNames& names, std::pair* out) const; std::unique_ptr parseExprOrDefine(Value value, const Position& position, LocalNames& names); std::unique_ptr parseExpr(Value value, const Position& position, LocalNames& names); diff --git a/src/translator.cpp b/src/translator.cpp new file mode 100644 index 0000000..3fd750d --- /dev/null +++ b/src/translator.cpp @@ -0,0 +1,101 @@ +#include "translator.hpp" +#include + + +namespace nscheme { + + +bool SyntaxRule::match(Value pattern, Value expr, std::unordered_map& mapping) const +{ + if (pattern.isSymbol()) { + Symbol symbol = pattern.asSymbol(); + if (symbol == kwd_underscore_) + return true; + auto it = std::find(literals_.begin(), literals_.end(), symbol); + if (it != literals_.end()) { + // symbol is literal + return expr.isSymbol() && symbol == expr.asSymbol(); + } + // symbol is not literal + mapping.insert(std::make_pair(symbol, expr)); + return true; + } + + if (isPair(pattern)) { + if (!isPair(expr)) + return false; + + ssize_t ellipsis_index = -1; + ssize_t pattern_len = -1; + bool is_proper = false; + + auto p = static_cast(pattern.asPointer()); + for (size_t i = 0;; ++i) { + Value car = p->getCar(); + if (car.isSymbol() && car.asSymbol() == kwd_ellipsis_) { + if (ellipsis_index != -1) + throw std::runtime_error("multiple ellipsis in pattern"); + ellipsis_index = i; + } + + Value cdr = p->getCdr(); + if (!isPair(cdr)) { + pattern_len = i + 1; + if (cdr == Value::Nil) + is_proper = true; + break; + } + p = static_cast(cdr.asPointer()); + } + + ssize_t expr_len = -1; + auto q = static_cast(expr.asPointer()); + for (size_t i = 0;; ++i) { + Value cdr = q->getCdr(); + if (!isPair(cdr)) { + expr_len = i + 1; + if (is_proper != (cdr == Value::Nil)) + return false; + break; + } + q = static_cast(cdr.asPointer()); + } + + if (expr_len < pattern_len) + return false; + + ssize_t e = (ellipsis_index != -1) ? (ellipsis_index - 1) : (expr_len + 1); + ssize_t m = (ellipsis_index != -1) ? (expr_len - pattern_len + ellipsis_index) : -1; + + p = static_cast(pattern.asPointer()); + q = static_cast(expr.asPointer()); + for (ssize_t i = 0;; ++i) { + if (!match(p->getCar(), q->getCar(), mapping)) + return false; + + if (e <= i && i < m) { + Value q_cdr = q->getCdr(); + q = static_cast(q_cdr.asPointer()); + } + else { + Value p_cdr = (i == m) ? static_cast(p->getCdr().asPointer())->getCdr() + : p->getCdr(); + Value q_cdr = q->getCdr(); + if (!isPair(p_cdr) && !isPair(q_cdr)) { + if (is_proper) + return true; + return match(p_cdr, q_cdr, mapping); + } + if (!isPair(p_cdr) || !isPair(q_cdr)) + return false; + p = static_cast(p_cdr.asPointer()); + q = static_cast(q_cdr.asPointer()); + } + } + } + + return pattern == expr; +} + + +} // namespace nscheme diff --git a/src/translator.hpp b/src/translator.hpp new file mode 100644 index 0000000..753d32f --- /dev/null +++ b/src/translator.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include "parser.hpp" +#include "value.hpp" + + +namespace nscheme { + +class Allocator; +class SymbolTable; + + +class SyntaxRule { +public: + SyntaxRule(Value pattern, Value tmpl, std::vector& literals, LocalNames& names, + Allocator* allocator, SymbolTable* symbol_table) + : pattern_(pattern) + , template_(tmpl) + , literals_(literals) + , names_(names) + , allocator_(allocator) + , symbol_table_(symbol_table) + , kwd_underscore_(symbol_table->intern("-")) + , kwd_ellipsis_(symbol_table->intern("...")) + { + } + + bool verifyPattern() const; + + Value replace(Value expr); + + bool match(Value pattern, Value expr, std::unordered_map& mapping) const; + +private: + Value pattern_; + Value template_; + std::vector& literals_; + LocalNames& names_; + Allocator* allocator_; + SymbolTable* symbol_table_; + Symbol kwd_underscore_; + Symbol kwd_ellipsis_; +}; + + +} // namespace nscheme diff --git a/src/value.hpp b/src/value.hpp index 0da5449..a6bd2e5 100644 --- a/src/value.hpp +++ b/src/value.hpp @@ -84,7 +84,7 @@ class Value { union { uint64_t value_; - Object *ptr_; + Object* ptr_; }; }; diff --git a/test/translator.cpp b/test/translator.cpp new file mode 100644 index 0000000..9665526 --- /dev/null +++ b/test/translator.cpp @@ -0,0 +1,112 @@ +#include "translator.hpp" +#include "stream.hpp" +#include "scanner.hpp" +#include "reader.hpp" +#include "parser.hpp" +#include "symbol_table.hpp" +#include "source_map.hpp" +#include "gtest/gtest.h" +using namespace nscheme; + + +class SyntaxRuleTest : public ::testing::Test { +protected: + Value read(const std::string s) + { + StringStream stream(s, symbol_table_.intern(s)); + Scanner scanner(&stream, &symbol_table_); + Reader reader(&scanner, &symbol_table_, &allocator_, &source_map_); + return reader.read(); + } + + SymbolTable symbol_table_; + Allocator allocator_; + SourceMap source_map_; +}; + + +TEST_F(SyntaxRuleTest, SimpleSymbol) +{ + Value pattern = read("foo"); + Value tmpl = read("(set! foo 1)"); + std::vector literal; + LocalNames names(nullptr); + SyntaxRule rule(pattern, tmpl, literal, names, &allocator_, &symbol_table_); + + Value expr = read("my-symbol"); + std::unordered_map mapping; + bool r = rule.match(pattern, expr, mapping); + EXPECT_TRUE(r); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("foo"))); +} + + +TEST_F(SyntaxRuleTest, SimpleList) +{ + Value pattern = read("(_ foo bar baz)"); + Value tmpl = read("(set! foo bar)"); + std::vector literal; + LocalNames names(nullptr); + SyntaxRule rule(pattern, tmpl, literal, names, &allocator_, &symbol_table_); + + Value expr = read("(simple-macro my-symbol 100 #t)"); + std::unordered_map mapping; + bool r = rule.match(pattern, expr, mapping); + EXPECT_TRUE(r); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("foo"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("bar"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("baz"))); +} + + +TEST_F(SyntaxRuleTest, SimpleLiteral) +{ + Value pattern = read("(_ foo to bar)"); + Value tmpl = read("(set! foo bar)"); + std::vector literal = { symbol_table_.intern("to") }; + LocalNames names(nullptr); + SyntaxRule rule(pattern, tmpl, literal, names, &allocator_, &symbol_table_); + + Value expr = read("(simple-macro my-symbol to 100)"); + std::unordered_map mapping; + bool r = rule.match(pattern, expr, mapping); + EXPECT_TRUE(r); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("foo"))); + EXPECT_EQ(0, mapping.count(symbol_table_.intern("to"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("bar"))); +} + + +TEST_F(SyntaxRuleTest, ComplexList1) +{ + Value pattern = read("(_ foo bar ... baz)"); + Value tmpl = read("(set! foo bar)"); + std::vector literal; + LocalNames names(nullptr); + SyntaxRule rule(pattern, tmpl, literal, names, &allocator_, &symbol_table_); + + Value expr = read("(simple-macro s1 s2 s3 s4 s5 s6)"); + std::unordered_map mapping; + bool r = rule.match(pattern, expr, mapping); + EXPECT_TRUE(r); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("foo"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("bar"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("baz"))); +} + + +TEST_F(SyntaxRuleTest, ComplexList2) +{ + Value pattern = read("(_ foo bar ...)"); + Value tmpl = read("(set! foo bar)"); + std::vector literal; + LocalNames names(nullptr); + SyntaxRule rule(pattern, tmpl, literal, names, &allocator_, &symbol_table_); + + Value expr = read("(simple-macro s1 s2 s3 s4 s5 s6)"); + std::unordered_map mapping; + bool r = rule.match(pattern, expr, mapping); + EXPECT_TRUE(r); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("foo"))); + EXPECT_EQ(1, mapping.count(symbol_table_.intern("bar"))); +} diff --git a/test/value.cpp b/test/value.cpp index 4d5950e..d5a134c 100644 --- a/test/value.cpp +++ b/test/value.cpp @@ -2,6 +2,7 @@ #include "gtest/gtest.h" using namespace nscheme; + TEST(ValueTest, Integer) { Value v = Value::fromInteger(42);