@@ -2,5 +2,7 @@ cmake_minimum_required(VERSION 3.14)
|
|||||||
|
|
||||||
project(furlang VERSION 0.0.0 LANGUAGES CXX)
|
project(furlang VERSION 0.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
find_package(GTest REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(furlang)
|
add_subdirectory(furlang)
|
||||||
add_subdirectory(furc)
|
add_subdirectory(furc)
|
||||||
+14
-3
@@ -1,4 +1,15 @@
|
|||||||
file(GLOB_RECURSE FURC_SRCS "src/**.cpp")
|
file(GLOB_RECURSE FURC_SRCS "src/**.cpp")
|
||||||
file(GLOB_RECURSE FURC_HDRS "src/**.cpp")
|
file(GLOB_RECURSE FURC_HDRS "include/**.hpp")
|
||||||
add_executable(furc ${FURC_SRCS} ${FURC_HDRS})
|
add_library(libfurc ${FURC_SRCS} ${FURC_HDRS})
|
||||||
target_include_directories(furc PRIVATE include/)
|
target_include_directories(libfurc PUBLIC include/)
|
||||||
|
target_compile_definitions(libfurc PRIVATE LIBFURC)
|
||||||
|
set_target_properties(libfurc PROPERTIES PREFIX "")
|
||||||
|
add_executable(furc src/main.cpp)
|
||||||
|
target_link_libraries(furc PRIVATE libfurc)
|
||||||
|
|
||||||
|
include(GoogleTest)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE FURC_TESTS "test/**.cpp")
|
||||||
|
add_executable(furc_tests ${FURC_TESTS})
|
||||||
|
target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main)
|
||||||
|
gtest_discover_tests(furc_tests)
|
||||||
@@ -12,7 +12,6 @@ namespace front {
|
|||||||
|
|
||||||
enum class token_t {
|
enum class token_t {
|
||||||
None,
|
None,
|
||||||
Error,
|
|
||||||
Identifier,
|
Identifier,
|
||||||
Keyword,
|
Keyword,
|
||||||
Integer,
|
Integer,
|
||||||
@@ -28,14 +27,9 @@ enum class token_t {
|
|||||||
Dot,
|
Dot,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool is_token_type_empty(token_t type) {
|
|
||||||
return type == token_t::None || type == token_t::Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case token_t::None: return os << "none";
|
case token_t::None: return os << "none";
|
||||||
case token_t::Error: return os << "error";
|
|
||||||
case token_t::Identifier: return os << "identifier";
|
case token_t::Identifier: return os << "identifier";
|
||||||
case token_t::Keyword: return os << "keyword";
|
case token_t::Keyword: return os << "keyword";
|
||||||
case token_t::Integer: return os << "integer";
|
case token_t::Integer: return os << "integer";
|
||||||
@@ -55,7 +49,6 @@ static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
|||||||
static inline std::string operator+(const std::string& str, token_t type) {
|
static inline std::string operator+(const std::string& str, token_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case token_t::None: return str + "none";
|
case token_t::None: return str + "none";
|
||||||
case token_t::Error: return str + "error";
|
|
||||||
case token_t::Identifier: return str + "identifier";
|
case token_t::Identifier: return str + "identifier";
|
||||||
case token_t::Keyword: return str + "keyword";
|
case token_t::Keyword: return str + "keyword";
|
||||||
case token_t::Integer: return str + "integer";
|
case token_t::Integer: return str + "integer";
|
||||||
@@ -72,18 +65,6 @@ static inline std::string operator+(const std::string& str, token_t type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class error_token {
|
|
||||||
None,
|
|
||||||
UnexpectedCharacter,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline std::ostream& operator<<(std::ostream& os, error_token error) {
|
|
||||||
switch (error) {
|
|
||||||
case error_token::None: return os << "none";
|
|
||||||
case error_token::UnexpectedCharacter: return os << "unexpected character";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class keyword_token {
|
enum class keyword_token {
|
||||||
None,
|
None,
|
||||||
Func,
|
Func,
|
||||||
@@ -108,7 +89,6 @@ static inline std::string operator+(const std::string& str, keyword_token keywor
|
|||||||
|
|
||||||
struct token {
|
struct token {
|
||||||
token_t type = token_t::None;
|
token_t type = token_t::None;
|
||||||
error_token error = error_token::None;
|
|
||||||
keyword_token keyword = keyword_token::None;
|
keyword_token keyword = keyword_token::None;
|
||||||
std::string_view value;
|
std::string_view value;
|
||||||
|
|
||||||
@@ -117,25 +97,36 @@ struct token {
|
|||||||
token(token_t type, std::string_view value = {})
|
token(token_t type, std::string_view value = {})
|
||||||
: type(type), value(value) {}
|
: type(type), value(value) {}
|
||||||
|
|
||||||
token(error_token error, std::string_view value = {})
|
|
||||||
: type(token_t::Error), error(error), value(value) {}
|
|
||||||
|
|
||||||
token(keyword_token keyword, std::string_view value = {})
|
token(keyword_token keyword, std::string_view value = {})
|
||||||
: type(token_t::Keyword), keyword(keyword), value(value) {}
|
: type(token_t::Keyword), keyword(keyword), value(value) {}
|
||||||
|
|
||||||
|
bool operator==(const token& rhs) const {
|
||||||
|
if (type != rhs.type) return false;
|
||||||
|
switch (type) {
|
||||||
|
case token_t::None:
|
||||||
|
case token_t::Identifier: return value == rhs.value;
|
||||||
|
case token_t::Keyword: return keyword == rhs.keyword;
|
||||||
|
case token_t::Integer: return value == rhs.value;
|
||||||
|
case token_t::Lparen:
|
||||||
|
case token_t::Rparen:
|
||||||
|
case token_t::Lbrace:
|
||||||
|
case token_t::Rbrace:
|
||||||
|
case token_t::Lbracket:
|
||||||
|
case token_t::Rbracket:
|
||||||
|
case token_t::Semicolon:
|
||||||
|
case token_t::Colon:
|
||||||
|
case token_t::Comma:
|
||||||
|
case token_t::Dot: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline std::ostream& operator<<(std::ostream& os, const token& token) {
|
static inline std::ostream& operator<<(std::ostream& os, const token& token) {
|
||||||
os << token.type;
|
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case token_t::Error: {
|
|
||||||
os << ": " << token.error;
|
|
||||||
if (!token.value.empty()) os << " \"" << token.value << '"';
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
case token_t::Identifier:
|
case token_t::Identifier:
|
||||||
case token_t::Keyword:
|
case token_t::Integer: return os << token.value;
|
||||||
case token_t::Integer: return os << ": " << token.value;
|
case token_t::Keyword: return os << token.keyword;
|
||||||
default: return os;
|
default: return os << token.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,10 +76,12 @@ public:
|
|||||||
operator const_reference() const { return *m_value; }
|
operator const_reference() const { return *m_value; }
|
||||||
operator Error() const { return m_error; }
|
operator Error() const { return m_error; }
|
||||||
|
|
||||||
reference operator*() { return m_value; }
|
reference operator*() { return *m_value; }
|
||||||
const_reference operator*() const { return m_value; }
|
const_reference operator*() const { return *m_value; }
|
||||||
pointer operator->() { return &*m_value; }
|
pointer operator->() { return &*m_value; }
|
||||||
const_pointer operator->() const { return &*m_value; }
|
const_pointer operator->() const { return &*m_value; }
|
||||||
|
public:
|
||||||
|
bool operator==(const T& rhs) const { return m_value.has_value() && *m_value == rhs; }
|
||||||
public:
|
public:
|
||||||
friend std::ostream& operator<<(std::ostream& os, const handle& result) {
|
friend std::ostream& operator<<(std::ostream& os, const handle& result) {
|
||||||
os << result.m_location << ": ";
|
os << result.m_location << ": ";
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#include "furc/front/lexer.hpp"
|
#include "furc/front/lexer.hpp"
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace furc::front {
|
namespace furc::front {
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
lexer::lexer(std::string_view filename, std::string_view content)
|
lexer::lexer(std::string_view filename, std::string_view content)
|
||||||
: m_filename(filename), m_content(content) {}
|
: m_filename(filename), m_content(content) {}
|
||||||
|
|
||||||
@@ -45,7 +48,7 @@ token_handle<> lexer::next_token() {
|
|||||||
return { location, token_t::Identifier, value };
|
return { location, token_t::Identifier, value };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { location, error_token::UnexpectedCharacter, m_content.substr(m_cursor, 1) };
|
return { location, "unexpected character '"s.append(m_content.substr(m_cursor, 1)) + "'" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ ast::node_handle<ast::declaration_node> parser::parse_declaration() {
|
|||||||
return ast::node_handle<ast::function_declarartion_node>{ first.location(), name };
|
return ast::node_handle<ast::function_declarartion_node>{ first.location(), name };
|
||||||
}
|
}
|
||||||
case token_t::None:
|
case token_t::None:
|
||||||
case token_t::Error:
|
|
||||||
case token_t::Identifier:
|
case token_t::Identifier:
|
||||||
case token_t::Keyword:
|
case token_t::Keyword:
|
||||||
case token_t::Integer:
|
case token_t::Integer:
|
||||||
@@ -84,7 +83,6 @@ ast::node_handle<ast::declaration_node> parser::parse_declaration() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case token_t::None:
|
case token_t::None:
|
||||||
case token_t::Error:
|
|
||||||
case token_t::Identifier:
|
case token_t::Identifier:
|
||||||
case token_t::Integer:
|
case token_t::Integer:
|
||||||
case token_t::Lparen:
|
case token_t::Lparen:
|
||||||
|
|||||||
+5
-1
@@ -1,3 +1,5 @@
|
|||||||
|
#ifndef LIBFURC
|
||||||
|
|
||||||
#include "furc/front/parser.hpp"
|
#include "furc/front/parser.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -7,4 +9,6 @@ int main(void) {
|
|||||||
std::cout << parser.parse() << '\n';
|
std::cout << parser.parse() << '\n';
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // LIBFURC
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "furc/front/lexer.hpp"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using namespace furc::front;
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
#define EXPECT_TOKEN(lexer, t) EXPECT_EQ((lexer).next_token(), (t));
|
||||||
|
|
||||||
|
TEST(Lexer, Tokens) {
|
||||||
|
lexer lexer("<TEMP>", "()\n\t\t{\n}[]; :,.main return func");
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Lparen, "("sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Rparen, ")"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Lbrace, "{"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Rbrace, "}"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Lbracket, "["sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Rbracket, "]"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Semicolon, ";"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Colon, ":"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Comma, ","sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Dot, "."sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ token_t::Identifier, "main"sv }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ keyword_token::Return }));
|
||||||
|
EXPECT_TOKEN(lexer, (token{ keyword_token::Func }));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user