alecAbstraction Layer for Escape Codes |
git clone git://git.dimitrijedobrota.com/alec.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | d705309b300eaf8f7c60a6eb6774f21e5634789c |
parent | 4f09fd258caac125fa751b1d4864d1276c77076e |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Tue, 20 May 2025 02:36:57 +0200 |
Get rid of code generation...
M | .clang-format | | | +++++++++ --- |
M | .clang-tidy | | | ++++++++++++++++++++++++++++++++ ------------- |
M | CMakeLists.txt | | | ++++++ ------------------------------------------------------- |
M | CMakePresets.json | | | +++ -- |
M | cmake/install-config.cmake | | | ++ -- |
M | cmake/install-rules.cmake | | | + - |
M | example/alec_compile.cpp | | | ++++ ------- |
M | example/alec_runtime.cpp | | | ++++++++++ ---------- |
A | include/alec/alec.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | include/alec/terminal.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | source/alec.rules.hpp | | | --------------------------------------------------------------------------------- |
D | source/driver.hpp | | | -------------------------------------- |
D | source/generator.cpp | | | --------------------------------------------------------------------------------- |
D | source/generator.h | | | ------------------- |
D | source/lexer.l | | | --------------------------------------------------------------------------------- |
D | source/location.hpp | | | ------------------ |
D | source/parser.y | | | --------------------------------------------------------------------------------- |
17 files changed, 681 insertions(+), 1149 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -2,12 +2,12 @@
Language: Cpp
# BasedOnStyle: Chromium
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: BlockIndent
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignEscapedNewlines: Right
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
@@ -17,9 +17,10 @@
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortLambdasOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
@@ -50,6 +51,7 @@
BreakBeforeBraces: Custom
# BreakBeforeInheritanceComma: true
BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true
BreakBeforeConceptDeclarations: Always
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: true
@@ -70,6 +72,7 @@
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
- BASED_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
# Standard library headers come before anything else
@@ -90,6 +93,8 @@
IndentPPDirectives: AfterHash
IndentExternBlock: NoIndent
IndentWidth: 2
IndentWrappedFunctionNames: false
IndentRequiresClause: true
RequiresClausePosition: OwnLine
InsertTrailingCommas: Wrapped
JavaScriptQuotes: Double
JavaScriptWrapImports: true
@@ -98,6 +103,7 @@
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
RequiresExpressionIndentation: OuterScope
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
diff --git a/ .clang-tidy b/ .clang-tidy
@@ -3,21 +3,26 @@
# misc-non-private-member-variables-in-classes: the options don't do anything
# modernize-use-nodiscard: too aggressive, attribute is situationally useful
Checks: "*,\
-google-readability-todo,\
-altera-*,\
-cppcoreguidelines-avoid-magic-numbers,\
-boost*,\
-cppcoreguidelines-avoid-do-while,\
-cppcoreguidelines-pro-bounds-constant-array-index,\
-fuchsia-*,\
fuchsia-multiple-inheritance,\
-google-readability-todo,\
-llvm-header-guard,\
-llvm-include-order,\
-llvmlibc-*,\
-modernize-use-nodiscard,\
-misc-include-cleaner,\
-misc-non-private-member-variables-in-classes,\
-misc-no-recursion,\
-modernize-use-trailing-return-type,\
-misc-non-private-member-variables-in-classes,
-readability-magic-numbers
-readability-suspicious-call-argument,\
-*-ranges,\
-*magic*,\
-cppcoreguidelines-missing-std-forward,\
-cppcoreguidelines-rvalue-reference-param-not-moved,\
"
WarningsAsErrors: ''
ExcludeHeaderFilterRegex: 'parser.hpp'
CheckOptions:
- key: 'bugprone-argument-comment.StrictMode'
value: 'true'
@@ -76,9 +81,9 @@
CheckOptions:
- key: 'readability-identifier-naming.ConstexprVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumCase'
value: 'CamelCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumConstantCase'
value: 'UPPER_CASE'
value: 'lower_case'
- key: 'readability-identifier-naming.FunctionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalConstantCase'
@@ -132,7 +137,7 @@
CheckOptions:
- key: 'readability-identifier-naming.PublicMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ScopedEnumConstantCase'
value: 'UPPER_CASE'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticVariableCase'
@@ -140,15 +145,17 @@
CheckOptions:
- key: 'readability-identifier-naming.StructCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TemplateParameterCase'
value: 'lower_case'
value: 'CamelCase'
- key: 'readability-identifier-naming.TemplateTemplateParameterCase'
value: 'lower_case'
value: 'CamelCase'
- key: 'readability-identifier-naming.TypeAliasCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TypeAliasIgnoredRegexp'
value: 'N'
- key: 'readability-identifier-naming.TypedefCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TypeTemplateParameterCase'
value: 'lower_case'
value: 'CamelCase'
- key: 'readability-identifier-naming.UnionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ValueTemplateParameterCase'
@@ -157,4 +164,16 @@
CheckOptions:
value: 'lower_case'
- key: 'readability-identifier-naming.VirtualMethodCase'
value: 'lower_case'
- key: 'readability-identifier-length.IgnoredVariableNames'
value: "^[abcdxyznm]$"
- key: 'readability-identifier-length.IgnoredParameterNames'
value: "^[abcdxyznm]$"
- key: 'google-runtime-int.UnsignedTypePrefix'
value: "u"
- key: 'google-runtime-int.SignedTypePrefix'
value: "i"
- key: 'cppcoreguidelines-missing-std-forward.ForwardFunction'
value: "::based::forward"
- key: 'cppcoreguidelines-rvalue-reference-param-not-moved.MoveFunction'
value: "::based::move"
...
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -4,7 +4,7 @@
include(cmake/prelude.cmake)
project(
alec
VERSION 0.1.18
VERSION 0.2.0
DESCRIPTION "Abstraction Layer for Escape Codes"
HOMEPAGE_URL "git://git.dimitrijedobrota.com/alec.git"
LANGUAGES CXX
@@ -13,63 +13,14 @@
project(
include(cmake/project-is-top-level.cmake)
include(cmake/variables.cmake)
# ---- Build library ----
set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}/source")
file(MAKE_DIRECTORY ${PARSER_DIR})
find_package(FLEX)
find_package(BISON)
set(LEXER_OUT "${PARSER_DIR}/lexer.cpp")
set(PARSER_OUT "${PARSER_DIR}/parser.cpp")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(FLAGS "--debug")
endif()
FLEX_TARGET(LEXER source/lexer.l "${LEXER_OUT}" DEFINES_FILE "${PARSER_DIR}/scanner.hpp" COMPILE_FLAGS "${FLAGS}")
BISON_TARGET(PARSER source/parser.y "${PARSER_OUT}" DEFINES_FILE "${PARSER_DIR}/parser.hpp" COMPILE_FLAGS "${FLAGS}")
ADD_FLEX_BISON_DEPENDENCY(LEXER PARSER)
set_source_files_properties(${LEXER_OUT} PROPERTIES SKIP_LINTING ON)
set_source_files_properties(${PARSER_OUT} PROPERTIES SKIP_LINTING ON)
add_executable(generator "${LEXER_OUT}" "${PARSER_OUT}" source/generator.cpp)
target_include_directories(generator PRIVATE source ${PARSER_DIR})
find_package(cemplate CONFIG REQUIRED)
target_link_libraries(generator PRIVATE cemplate::cemplate)
target_compile_features(generator PUBLIC cxx_std_20)
set(GENERATE_OUT_BIN "${PROJECT_BINARY_DIR}/bin")
set(GENERATE_OUT_INCLUDE "${PROJECT_BINARY_DIR}/include/alec")
file(MAKE_DIRECTORY ${GENERATE_OUT_BIN} ${GENERATE_OUT_INCLUDE})
set_target_properties(generator PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT_BIN}"
)
set(RULES_NAME "alec.rules.hpp")
set(RULES_FILE "${GENERATE_OUT_INCLUDE}/${RULES_NAME}")
configure_file(source/alec.rules.hpp ${RULES_FILE} COPYONLY)
add_custom_command(
OUTPUT ${GENERATE_OUT_INCLUDE}/alec.hpp
COMMAND generator ${RULES_FILE} > ${GENERATE_OUT_INCLUDE}/alec.hpp
DEPENDS generator source/${RULES_NAME}
COMMENT "Generating include file"
)
# ---- Declare library ----
add_library(alec_alec INTERFACE ${GENERATE_OUT_INCLUDE}/alec.hpp)
add_library(alec_alec INTERFACE)
add_library(alec::alec ALIAS alec_alec)
find_package(based 0.1.2 CONFIG REQUIRED)
target_link_libraries(alec_alec INTERFACE based::based)
set_property(
TARGET alec_alec PROPERTY
EXPORT_NAME alec
@@ -78,7 +29,7 @@
set_property(
target_include_directories(
alec_alec ${warning_guard}
INTERFACE
"\$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
"\$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)
target_compile_features(alec_alec INTERFACE cxx_std_20)
diff --git a/ CMakePresets.json b/ CMakePresets.json
@@ -75,7 +75,8 @@
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/sdl /guard:cf /utf-8 /diagnostics:caret /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /permissive- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:enumTypes /Zc:lambda /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc",
"CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf"
"CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf",
"CMAKE_SHARED_LINKER_FLAGS": "/machine:x64 /guard:cf"
}
},
{
@@ -111,7 +112,7 @@
"cacheVariables": {
"ENABLE_COVERAGE": "ON",
"CMAKE_BUILD_TYPE": "Coverage",
"CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -fkeep-inline-functions -fkeep-static-functions",
"CMAKE_CXX_FLAGS_COVERAGE": "-O0 -g --coverage -fkeep-inline-functions -fkeep-static-functions",
"CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage",
"CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage",
"CMAKE_MAP_IMPORTED_CONFIG_COVERAGE": "Coverage;RelWithDebInfo;Release;Debug;"
diff --git a/ cmake/install-config.cmake b/ cmake/install-config.cmake
@@ -1,6 +1,6 @@
include(CMakeFindDependencyMacro)
find_dependency(cemplate)
find_dependency(based)
if(cemplate_FOUND)
if(based_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/alecTargets.cmake")
endif()
diff --git a/ cmake/install-rules.cmake b/ cmake/install-rules.cmake
@@ -16,7 +16,7 @@
include(GNUInstallDirs)
set(package alec)
install(
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
DIRECTORY include/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT alec_Development
)
diff --git a/ example/alec_compile.cpp b/ example/alec_compile.cpp
@@ -5,9 +5,6 @@
using namespace alec; // NOLINT
using enum Color;
using enum Decor;
int main()
{
std::cout << abuf_enable_v << cursor_hide_v;
@@ -17,11 +14,11 @@
int main()
std::cout << cursor_down_v<3>;
std::cout << foreground_v<30> << background_v<196, 53, 64> << "WORLD!\n";
std::cout << background_v<DEFAULT> << "testing 1...\n"
<< foreground_v<DEFAULT>;
std::cout << background_v<color::def> << "testing 1...\n"
<< foreground_v<color::def>;
std::cout << decor_set_v<INVERSE> << "testing 2...\n"
<< decor_reset_v<INVERSE>;
std::cout << decor_set_v<decor::inverse> << "testing 2...\n"
<< decor_reset_v<decor::inverse>;
std::cout << cursor_up_v<5> << "Hello there!" << cursor_save_v;
std::cout << cursor_down_v<10> << "General Kenobi!";
diff --git a/ example/alec_runtime.cpp b/ example/alec_runtime.cpp
@@ -5,29 +5,29 @@
using namespace alec; // NOLINT
using enum Color;
using enum Decor;
int main()
{
std::cout << abuf_enable() << cursor_hide();
std::cout << abuf_enable_v << cursor_hide_v;
std::cout << cursor_position(1, 1) << foreground(91) << "HELLO!\n";
std::cout << cursor_down(3);
std::cout << foreground(30) << background(96, 53, 64) << "WORLD!\n";
std::cout << background(DEFAULT) << "testing 1...\n" << foreground(DEFAULT);
std::cout << decor_set(INVERSE) << "testing 2...\n" << decor_reset(INVERSE);
std::cout << background(color::def) << "testing 1...\n"
<< foreground(color::def);
std::cout << decor_set(decor::inverse) << "testing 2...\n"
<< decor_reset(decor::inverse);
std::cout << cursor_up(5) << "Hello there!" << cursor_save();
std::cout << cursor_up(5) << "Hello there!" << cursor_save_v;
std::cout << cursor_down(10) << "General Kenobi!";
std::cout << cursor_position(10, 40) << "no pain no gain" << cursor_restore()
<< cursor_show();
std::cout << cursor_position(10, 40) << "no pain no gain" << cursor_restore_v
<< cursor_show_v;
(void)std::getchar();
std::cout << abuf_disable();
std::cout << abuf_disable_v;
return 0;
}
diff --git a/ include/alec/alec.hpp b/ include/alec/alec.hpp
@@ -0,0 +1,363 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <string>
#include <based/enum/enum.hpp>
#include <based/string/literal.hpp>
#include <based/types/types.hpp>
namespace alec
{
#define ENUM_COLOR \
black, red, green, yellow, blue, magenta, cyan, white, unused_1, def
BASED_DECLARE_ENUM(color, based::bi8, 0, ENUM_COLOR)
BASED_DEFINE_ENUM(color, based::bi8, 0, ENUM_COLOR)
#undef ENUM_COLOR
#define ENUM_DECOR \
reset, bold, dim, italic, underline, blink, unused_1, inverse, hide, strike
BASED_DECLARE_ENUM(decor, based::bi8, 0, ENUM_DECOR)
BASED_DEFINE_ENUM(decor, based::bi8, 0, ENUM_DECOR)
#undef ENUM_DECOR
#define ENUM_MOTION end, begin, whole
BASED_DECLARE_ENUM(motion, based::bi8, 0, ENUM_MOTION)
BASED_DEFINE_ENUM(motion, based::bi8, 0, ENUM_MOTION)
#undef ENUM_MOTION
namespace detail
{
namespace helper
{
template<std::size_t n>
static constexpr std::size_t size(based::string_literal<n> /*val*/)
{
return n;
}
static constexpr std::size_t size(char /*val*/)
{
return 1;
}
template<class T>
static constexpr std::size_t size(T val)
{
std::size_t len = 1;
while ((val /= 10) != 0) {
len++;
}
return len;
}
template<std::size_t n>
static constexpr char* append(char* ptr, based::string_literal<n> val)
{
std::copy_n(val.data(), n, ptr);
return ptr + n; // NOLINT
}
static constexpr char* append(char* ptr, char val)
{
*ptr++ = val; // NOLINT
return ptr;
}
template<class T>
static constexpr char* append(char* ptr, T val)
{
char* tmp = ptr += size(val); // NOLINT
do { // NOLINT
*--tmp = '0' + static_cast<char>(val % 10); // NOLINT
} while ((val /= 10) != 0);
return ptr;
}
static constexpr std::string make(auto... args)
{
std::string res((helper::size(args) + ... + 2), 0);
res[0] = 0x1B, res[1] = '[';
auto* ptr = res.data() + 2; // NOLINT
((ptr = helper::append(ptr, args)), ...);
return res;
}
template<auto... args>
struct escape_t
{
static constexpr const auto value = []()
{
std::array<char, (helper::size(args) + ... + 3)> arr = {};
arr[0] = 0x1B, arr[1] = '[';
auto* ptr = arr.data() + 2;
((ptr = helper::append(ptr, args)), ...);
return arr;
}();
static constexpr auto data = value.data();
};
} // namespace helper
template<auto... args>
static constexpr auto escape = alec::detail::helper::escape_t<args...>::data;
template<based::string_literal... strs>
static constexpr auto escape_literal = escape<strs...>;
} // namespace detail
// Template compile-time variables
// Forward-declare templates
template<auto... val>
static const char* const background_v = "";
template<auto... val>
static const char* const foreground_v = "";
// Template specializations
// Move cursor up/down/frwd/back
template<unsigned cnt>
static constexpr auto cursor_up_v = detail::escape<cnt, 'A'>;
template<unsigned cnt>
static constexpr auto cursor_down_v = detail::escape<cnt, 'B'>;
template<unsigned cnt>
static constexpr auto cursor_frwd_v = detail::escape<cnt, 'C'>;
template<unsigned cnt>
static constexpr auto cursor_back_v = detail::escape<cnt, 'D'>;
// Move cursor to the next/prev line
template<unsigned cnt>
static constexpr auto cursor_line_next_v = detail::escape<cnt, 'E'>;
template<unsigned cnt>
static constexpr auto cursor_line_prev_v = detail::escape<cnt, 'F'>;
// Set cursor to specific column
template<unsigned col>
static constexpr auto cursor_column_v = detail::escape<col, 'G'>;
// Erase functions
template<motion::type motion>
static constexpr auto erase_display_v = detail::escape<motion.value, 'J'>;
template<motion::type motion>
static constexpr auto erase_line_v = detail::escape<motion.value, 'K'>;
// Scroll up/down
template<unsigned cnt>
static constexpr auto scroll_up_v = detail::escape<cnt, 'S'>;
template<unsigned cnt>
static constexpr auto scroll_down_v = detail::escape<cnt, 'T'>;
// Set cursor to a specific position
template<unsigned row, unsigned col>
static constexpr auto cursor_position_v = detail::escape<row, ';', col, 'H'>;
// color
// palet colors
template<color::type color>
static constexpr auto foreground_v<color> =
detail::escape<color.value + 30, 'm'>;
template<color::type color>
static constexpr auto background_v<color> =
detail::escape<color.value + 40, 'm'>;
// 256-color palette
template<based::bu8 idx>
static constexpr auto foreground_v<idx> =
detail::escape<38, ';', 5, ';', idx, 'm'>;
template<based::bu8 idx>
static constexpr auto background_v<idx> =
detail::escape<48, ';', 5, ';', idx, 'm'>;
// RGB colors
template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto foreground_v<red, green, blue> =
detail::escape<38, ';', 2, ';', red, ';', green, ';', blue, 'm'>;
template<based::bu8 red, based::bu8 green, based::bu8 blue>
static constexpr auto background_v<red, green, blue> =
detail::escape<48, ';', 2, ';', red, ';', green, ';', blue, 'm'>;
// Set/reset text decorators
template<decor::type decor>
static constexpr auto decor_set_v = detail::escape<decor.value, 'm'>;
template<decor::type decor>
static constexpr auto decor_reset_v = detail::escape<decor.value + 20, 'm'>;
// Save/restore cursor position;
static constexpr auto cursor_save_v = detail::escape<'s'>;
static constexpr auto cursor_restore_v = detail::escape<'u'>;
// Set screen modes
template<unsigned mode>
static constexpr auto screen_mode_set_v = detail::escape<'=', mode, 'h'>;
template<unsigned mode>
static constexpr auto screen_mode_reset_v = detail::escape<'=', mode, 'l'>;
// Private screen modes supported by most terminals
// Save/restore screen
static constexpr auto screen_save_v = detail::escape_literal<"?47h">;
static constexpr auto screen_restore_v = detail::escape_literal<"?47l">;
// Show/hide cursor
static constexpr auto cursor_show_v = detail::escape_literal<"?25h">;
static constexpr auto cursor_hide_v = detail::escape_literal<"?25l">;
// Enable/disable alternate buffer
static constexpr auto abuf_enable_v = detail::escape_literal<"?1049h">;
static constexpr auto abuf_disable_v = detail::escape_literal<"?1049l">;
// Enable/disable bracketed paste mode
static constexpr auto paste_enable_v = detail::escape_literal<"?2004h">;
static constexpr auto paste_disable_v = detail::escape_literal<"?2004l">;
// Run-time functions
// Move cursor up/down/frwd/back
static constexpr auto cursor_up(unsigned cnt)
{
return detail::helper::make(cnt, 'A');
}
static constexpr auto cursor_down(unsigned cnt)
{
return detail::helper::make(cnt, 'B');
}
static constexpr auto cursor_frwd(unsigned cnt)
{
return detail::helper::make(cnt, 'C');
}
static constexpr auto cursor_back(unsigned cnt)
{
return detail::helper::make(cnt, 'D');
}
// Move cursor to the next/prev line
static constexpr auto cursor_line_next(unsigned cnt)
{
return detail::helper::make(cnt, 'E');
}
static constexpr auto cursor_line_prev(unsigned cnt)
{
return detail::helper::make(cnt, 'F');
}
// Set cursor to specific column
static constexpr auto cursor_column(unsigned col)
{
return detail::helper::make(col, 'G');
}
// Erase functions
static constexpr auto erase_display(motion::type motion)
{
return detail::helper::make(motion.value, 'J');
}
static constexpr auto erase_line(motion::type motion)
{
return detail::helper::make(motion.value, 'K');
}
// Scroll up/down
static constexpr auto scroll_up(unsigned cnt)
{
return detail::helper::make(cnt, 'S');
}
static constexpr auto scroll_down(unsigned cnt)
{
return detail::helper::make(cnt, 'T');
}
// Set cursor to a specific position
static constexpr auto cursor_position(unsigned row, unsigned col)
{
return detail::helper::make(row, ';', col, 'H');
}
// color
// palet colors
static constexpr auto foreground(color::type color)
{
return detail::helper::make(color.value + 30, 'm');
}
static constexpr auto background(color::type color)
{
return detail::helper::make(color.value + 40, 'm');
}
// 256-color palette
static constexpr auto foreground(based::bu8 idx)
{
return detail::helper::make(38, ';', 5, ';', idx, 'm');
}
static constexpr auto background(based::bu8 idx)
{
return detail::helper::make(48, ';', 5, ';', idx, 'm');
}
// RGB colors
static constexpr auto foreground(
based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::helper::make(38, ';', 2, ';', red, ';', green, ';', blue, 'm');
}
static constexpr auto background(
based::bu8 red, based::bu8 green, based::bu8 blue
)
{
return detail::helper::make(48, ';', 2, ';', red, ';', green, ';', blue, 'm');
}
// Set/reset text decorators
static constexpr auto decor_set(decor::type decor)
{
return detail::helper::make(decor.value, 'm');
}
static constexpr auto decor_reset(decor::type decor)
{
return detail::helper::make(decor.value + 20, 'm');
}
// Set screen modes
static constexpr auto screen_mode_set(unsigned mode)
{
return detail::helper::make('=', mode, 'h');
}
static constexpr auto screen_mode_reset(unsigned mode)
{
return detail::helper::make('=', mode, 'l');
}
} // namespace alec
diff --git a/ include/alec/terminal.hpp b/ include/alec/terminal.hpp
@@ -0,0 +1,251 @@
#pragma once
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
namespace alec
{
class runtime_error : public std::runtime_error
{
public:
explicit runtime_error(const std::string& err)
: std::runtime_error(err)
{
}
};
enum error_code_t // NOLINT
{
FDNTERM,
TERMIOSRD,
TERMIOSWR,
BUFFULL,
CHARRD,
IOCTL,
SCREENSZ
};
template<error_code_t e>
class error : public runtime_error
{
public:
explicit error()
: runtime_error(error_get_message(e))
{
}
private:
static std::string error_get_message(error_code_t error)
{
switch (error) {
case error_code_t::FDNTERM:
return "File descriptor is not associated with a terminal";
case error_code_t::TERMIOSRD:
return "Can't read termios";
case error_code_t::TERMIOSWR:
return "Can't write termios";
case error_code_t::BUFFULL:
return "Buffer is full";
case error_code_t::CHARRD:
return "Can't read character";
case error_code_t::IOCTL:
return "ioctl error";
case error_code_t::SCREENSZ:
return "Can't determine the screen size";
}
return "alec error, should not happen...";
}
};
class buffer
{
public:
explicit buffer(int fdsc)
: m_fd(fdsc)
, m_orig_termios()
{
if (isatty(m_fd) == 0) {
throw error<error_code_t::FDNTERM>();
}
if (tcgetattr(m_fd, &m_orig_termios) == -1) {
throw error<error_code_t::TERMIOSRD>();
}
struct termios raw = m_orig_termios;
// NOLINTBEGIN
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); // | ISIG
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
// NOLINTEND
/* put terminal in raw mode after flushing */
if (tcsetattr(m_fd, TCSAFLUSH, &raw) < 0) {
throw error<error_code_t::TERMIOSWR>();
}
}
buffer(const buffer&) = delete;
buffer& operator=(const buffer&) = delete;
buffer(buffer&&) = default;
buffer& operator=(buffer&&) = default;
~buffer() { tcsetattr(m_fd, TCSAFLUSH, &m_orig_termios); }
uint8_t read()
{
if (m_start == m_end && get() == 0) {
return 0;
}
uint8_t chr = m_buffer[m_start]; // NOLINT
m_start = (m_start + 1) % m_buffer.size();
return chr;
}
uint8_t read_blocking()
{
while (m_start == m_end) {
get();
}
uint8_t chr = m_buffer[m_start]; // NOLINT
m_start = (m_start + 1) % m_buffer.size();
return chr;
}
void flush()
{
while (get() != 0) {
}
}
private:
size_t get()
{
ssize_t scnt = -1;
if ((m_end + 1) % m_buffer.size() == m_start) {
throw error<error_code_t::BUFFULL>();
}
if (m_start <= m_end) {
scnt = ::read(m_fd, m_buffer.data() + m_end, m_buffer.size() - m_end);
} else {
scnt = ::read(m_fd, m_buffer.data() + m_end, m_start - m_end);
}
if (scnt == -1) {
throw error<error_code_t::CHARRD>();
}
const auto cnt = static_cast<size_t>(scnt);
m_end = (m_end + cnt) % m_buffer.size();
return cnt;
}
std::array<uint8_t, 1024> m_buffer = {0};
int m_fd = 0;
uint64_t m_start = 0;
uint64_t m_end = 0;
struct termios m_orig_termios;
};
inline auto& get_buffer()
{
static std::optional<buffer> ibuf;
return ibuf;
}
inline void init_buffer(int fdsc)
{
get_buffer().emplace(fdsc);
}
inline void dest_buffer()
{
get_buffer().reset();
}
inline std::pair<std::uint16_t, std::uint16_t> get_screen_size()
{
#ifdef TIOCGSIZE
struct ttysize tts = {};
if (ioctl(STDIN_FILENO, TIOCGSIZE, &tts) == -1) { // NOLINT
throw error<error_code_t::IOCTL>();
}
return {tts.ts_cols, tts.ts_lines};
#elif defined(TIOCGWINSZ)
struct winsize tts = {};
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &tts) == -1) { // NOLINT
throw error<error_code_t::IOCTL>();
}
return {tts.ws_col, tts.ws_row};
#endif /* TIOCGSIZE */
throw error<error_code_t::SCREENSZ>();
}
class event
{
public:
enum class Type : std::uint8_t
{
NONE = 0,
KEY = 1,
RESIZE = 2,
MOUSE = 3,
};
enum class Mod : std::uint8_t
{
ALT = 1,
CTRL = 2,
SHIFT = 4,
MOTION = 8,
};
event(Type type, uint8_t mod_mask, uint8_t key) // NOLINT
: m_type(type)
, m_mod_mask(mod_mask)
, m_key(key)
{
}
const auto& type() const { return m_type; }
auto& type() { return m_type; }
const auto& key() const { return m_key; }
auto& key() { return m_key; }
const auto& mod_mask() const { return m_mod_mask; }
auto& mod_mask() { return m_mod_mask; }
bool is_set(uint8_t mask) const { return mask == (m_mod_mask & mask); }
private:
Type m_type = Type::NONE;
uint8_t m_mod_mask = 0;
uint8_t m_key = 0;
};
inline event get_event()
{
const auto chr = get_buffer().value().read();
return {chr != 0 ? event::Type::KEY : event::Type::NONE, 0, chr};
}
} // namespace alec
diff --git a/ source/alec.rules.hpp b/ source/alec.rules.hpp
@@ -1,611 +0,0 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <string>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
namespace alec
{
enum Ctrl : std::uint8_t
{
BELL = 0x07,
BS = 0x08,
HT = 0x09,
LF = 0x0A,
VT = 0x0B,
FF = 0x0C,
CR = 0x0D,
ESC = 0x1B,
DEL = 0x7F,
};
enum class Color : std::uint8_t
{
BLACK = 0,
RED = 1,
GREEN = 2,
YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7,
DEFAULT = 9,
};
enum class Decor : std::uint8_t
{
RESET = 0,
BOLD = 1,
DIM = 2,
ITALIC = 3,
UNDERLINE = 4,
BLINK = 5,
INVERSE = 7,
HIDE = 8,
STRIKE = 9,
};
enum class Motion : std::uint8_t
{
END = 0,
BEGIN = 1,
WHOLE = 2,
};
namespace details
{
template<std::size_t n>
struct string_literal
{
constexpr string_literal(const char (&str)[n]) // NOLINT
: m_value(std::to_array(str))
{
} // NOLINT
constexpr std::size_t size() const { return n; }
constexpr const char* data() const { return m_value.data(); }
std::array<char, n> m_value;
};
namespace helper
{
template<std::size_t n>
static constexpr std::size_t size(string_literal<n> /*val*/)
{
return n;
}
static constexpr std::size_t size(char /*val*/)
{
return 1;
}
static constexpr std::size_t size(int val)
{
std::size_t len = 1;
while ((val /= 10) != 0) {
len++;
}
return len;
}
template<std::size_t n>
static constexpr char* append(char* ptr, string_literal<n> val)
{
std::copy_n(val.data(), n, ptr);
return ptr + n; // NOLINT
}
static constexpr char* append(char* ptr, char val)
{
*ptr++ = val; // NOLINT
return ptr;
}
static constexpr char* append(char* ptr, int val)
{
char* tmp = ptr += size(val); // NOLINT
do { // NOLINT
*--tmp = '0' + static_cast<char>(val % 10); // NOLINT
} while ((val /= 10) != 0);
return ptr;
}
static constexpr std::string make(auto... args)
{
std::string res((helper::size(args) + ... + 2), 0);
res[0] = Ctrl::ESC, res[1] = '[';
auto* ptr = res.data() + 2; // NOLINT
((ptr = helper::append(ptr, args)), ...);
return res;
}
template<auto... args>
struct escape_t
{
static constexpr const auto value = []()
{
std::array<char, (helper::size(args) + ... + 3)> arr = {Ctrl::ESC, '[', 0};
auto* ptr = arr.data() + 2;
((ptr = helper::append(ptr, args)), ...);
return arr;
}();
static constexpr auto data = value.data();
};
} // namespace helper
template<auto... args>
static constexpr auto escape = alec::details::helper::escape_t<args...>::data;
template<details::string_literal... strs>
static constexpr auto escape_literal = escape<strs...>;
} // namespace details
// Tamplate parameter constraints
template<int n>
concept limit_256_v = n >= 0 && n < 256;
template<int n>
concept limit_pos_v = n >= 0;
static constexpr bool limit_pos(int n)
{
return n >= 0;
}
static constexpr bool limit_256(int n)
{
return n >= 0 && n < 256;
}
/*%%*//*
// NOLINTBEGIN (*cast*)
// Move cursor up/down/frwd/back
cursor_up
int cnt
limit_pos
cnt, 'A'
cursor_down
int cnt
limit_pos
cnt, 'B'
cursor_frwd
int cnt
limit_pos
cnt, 'C'
cursor_back
int cnt
limit_pos
cnt, 'D'
// Move cursor to the next/prev line
cursor_line_next
int cnt
limit_pos
cnt, 'E'
cursor_line_prev
int cnt
limit_pos
cnt, 'F'
// Set cursor to specific column
cursor_column
int col
limit_pos
col, 'G'
// Erase functions
erase_display
Motion mtn
|
int(mtn), 'J'
erase_line
Motion mtn
|
int(mtn), 'K'
// Scroll up/down
scroll_up
int cnt
limit_pos
cnt, 'S'
scroll_down
int cnt
limit_pos
cnt, 'T'
// Set cursor to a specific position
cursor_position
int row, int col
limit_pos
row, ';', col, 'H'
// color
// palet colors
foreground
Color color
|
int(color) + 30, 'm'
background
Color color
|
int(color) + 40, 'm'
// 256-color palette
foreground
int idx
limit_256
38, ';', 5, ';', idx, 'm'
background
int idx
limit_256
48, ';', 5, ';', idx, 'm'
// RGB colors
foreground
int red, int green, int blue
limit_256
38, ';', 2, ';', red, ';', green, ';', blue, 'm'
background
int red, int green, int blue
limit_256
48, ';', 2, ';', red, ';', green, ';', blue, 'm'
// Set/reset text decorators
decor_set
Decor decor
|
int(decor), 'm'
decor_reset
Decor decor
|
int(decor) + 20, 'm'
// Save/restore cursor position;
cursor_save
|
|
's'
cursor_restore
|
|
'u'
// Set screen modes
screen_mode_set
int mode
limit_pos
'=', mode, 'h'
screen_mode_reset
int mode
limit_pos
'=', mode, 'l'
// Private screen modes supported by most terminals
// Save/restore screen
screen_save
|
|
"?47h"
screen_restore
|
|
"?47l"
// Show/hide cursor
cursor_show
|
|
"?25h"
cursor_hide
|
|
"?25l"
// Enable/disable alternate buffer
abuf_enable
|
|
"?1049h"
abuf_disable
|
|
"?1049l"
// Enable/disable bracketed paste mode
paste_enable
|
|
"?2004h"
paste_disable
|
|
"?2004l"
// NOLINTEND (*cast*)
*//*%%*/
class runtime_error : public std::runtime_error
{
public:
explicit runtime_error(const std::string& err)
: std::runtime_error(err)
{
}
};
enum error_code_t // NOLINT
{
FDNTERM,
TERMIOSRD,
TERMIOSWR,
BUFFULL,
CHARRD,
IOCTL,
SCREENSZ
};
template<error_code_t e>
class error : public runtime_error
{
public:
explicit error()
: runtime_error(error_get_message(e))
{
}
private:
static std::string error_get_message(error_code_t error)
{
switch (error) {
case error_code_t::FDNTERM:
return "File descriptor is not associated with a terminal";
case error_code_t::TERMIOSRD:
return "Can't read termios";
case error_code_t::TERMIOSWR:
return "Can't write termios";
case error_code_t::BUFFULL:
return "Buffer is full";
case error_code_t::CHARRD:
return "Can't read character";
case error_code_t::IOCTL:
return "ioctl error";
case error_code_t::SCREENSZ:
return "Can't determine the screen size";
}
return "alec error, should not happen...";
}
};
class buffer
{
public:
explicit buffer(int fdsc)
: m_fd(fdsc)
, m_orig_termios()
{
if (isatty(m_fd) == 0) {
throw error<error_code_t::FDNTERM>();
}
if (tcgetattr(m_fd, &m_orig_termios) == -1) {
throw error<error_code_t::TERMIOSRD>();
}
struct termios raw = m_orig_termios;
// NOLINTBEGIN
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); // | ISIG
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
// NOLINTEND
/* put terminal in raw mode after flushing */
if (tcsetattr(m_fd, TCSAFLUSH, &raw) < 0) {
throw error<error_code_t::TERMIOSWR>();
}
}
buffer(const buffer&) = delete;
buffer& operator=(const buffer&) = delete;
buffer(buffer&&) = default;
buffer& operator=(buffer&&) = default;
~buffer() { tcsetattr(m_fd, TCSAFLUSH, &m_orig_termios); }
uint8_t read()
{
if (m_start == m_end && get() == 0) {
return 0;
}
uint8_t chr = m_buffer[m_start]; // NOLINT
m_start = (m_start + 1) % m_buffer.size();
return chr;
}
uint8_t read_blocking()
{
while (m_start == m_end) {
get();
}
uint8_t chr = m_buffer[m_start]; // NOLINT
m_start = (m_start + 1) % m_buffer.size();
return chr;
}
void flush()
{
while (get() != 0) {
}
}
private:
size_t get()
{
ssize_t scnt = -1;
if ((m_end + 1) % m_buffer.size() == m_start) {
throw error<error_code_t::BUFFULL>();
}
if (m_start <= m_end) {
scnt = ::read(m_fd, m_buffer.data() + m_end, m_buffer.size() - m_end);
} else {
scnt = ::read(m_fd, m_buffer.data() + m_end, m_start - m_end);
}
if (scnt == -1) {
throw error<error_code_t::CHARRD>();
}
const auto cnt = static_cast<size_t>(scnt);
m_end = (m_end + cnt) % m_buffer.size();
return cnt;
}
std::array<uint8_t, 1024> m_buffer = {0};
int m_fd = 0;
uint64_t m_start = 0;
uint64_t m_end = 0;
struct termios m_orig_termios;
};
inline auto& get_buffer()
{
static std::optional<buffer> ibuf;
return ibuf;
}
inline void init_buffer(int fdsc)
{
get_buffer().emplace(fdsc);
}
inline void dest_buffer()
{
get_buffer().reset();
}
inline std::pair<std::uint16_t, std::uint16_t> get_screen_size()
{
#ifdef TIOCGSIZE
struct ttysize tts = {};
if (ioctl(STDIN_FILENO, TIOCGSIZE, &tts) == -1) { // NOLINT
throw error<error_code_t::IOCTL>();
}
return {tts.ts_cols, tts.ts_lines};
#elif defined(TIOCGWINSZ)
struct winsize tts = {};
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &tts) == -1) { // NOLINT
throw error<error_code_t::IOCTL>();
}
return {tts.ws_col, tts.ws_row};
#endif /* TIOCGSIZE */
throw error<error_code_t::SCREENSZ>();
}
class event
{
public:
enum class Type : std::uint8_t
{
NONE = 0,
KEY = 1,
RESIZE = 2,
MOUSE = 3,
};
enum class Mod : std::uint8_t
{
ALT = 1,
CTRL = 2,
SHIFT = 4,
MOTION = 8,
};
event(Type type, uint8_t mod_mask, uint8_t key) // NOLINT
: m_type(type)
, m_mod_mask(mod_mask)
, m_key(key)
{
}
const auto& type() const { return m_type; }
auto& type() { return m_type; }
const auto& key() const { return m_key; }
auto& key() { return m_key; }
const auto& mod_mask() const { return m_mod_mask; }
auto& mod_mask() { return m_mod_mask; }
bool is_set(uint8_t mask) const { return mask == (m_mod_mask & mask); }
private:
Type m_type = Type::NONE;
uint8_t m_mod_mask = 0;
uint8_t m_key = 0;
};
inline event get_event()
{
const auto chr = get_buffer().value().read();
return {chr != 0 ? event::Type::KEY : event::Type::NONE, 0, chr};
}
} // namespace alec
diff --git a/ source/driver.hpp b/ source/driver.hpp
@@ -1,38 +0,0 @@
#pragma once
#include <string>
#define yyFlexLexer yy_alec_FlexLexer
#if !defined(yyFlexLexerOnce)
# include <FlexLexer.h>
#endif
#include "location.hpp"
#include "parser.hpp"
namespace alec
{
class driver : public yy_alec_FlexLexer
{
int m_current_line = 0;
parser::semantic_type* m_yylval = nullptr;
location_t* m_yylloc = nullptr;
void copy_location()
{
*m_yylloc = location_t(m_current_line, m_current_line);
}
public:
driver(std::istream& ins, const bool debug)
: yy_alec_FlexLexer(&ins)
{
yy_alec_FlexLexer::set_debug(static_cast<int>(debug));
}
int yylex(parser::semantic_type* yylval, location_t* lloc);
};
} // namespace alec
diff --git a/ source/generator.cpp b/ source/generator.cpp
@@ -1,181 +0,0 @@
#include <cstring>
#include <fstream>
#include <iostream>
#include <set>
#include <span>
#include <string>
#include <vector>
#include "generator.h"
#include <cemplate/cemplate.hpp>
#include "driver.hpp"
namespace alec
{
extern std::vector<record> records; // NOLINT
extern std::vector<std::string> epilogue; // NOLINT
extern std::vector<std::string> prologue; // NOLINT
} // namespace alec
namespace
{
auto generate_dupes()
{
std::set<std::string> dupes;
std::set<std::string> seen;
for (const auto& record : alec::records) {
const auto [_, inserted] = seen.insert(record.name);
if (!inserted) {
dupes.insert(record.name);
}
}
return dupes;
}
void generate_variables()
{
using namespace cemplate; // NOLINT
Program prog(std::cout);
prog.comment("Template compile-time variables\n");
prog.comment("Forward-declare templates\n");
const auto dupes = generate_dupes();
for (const auto& dup : dupes) {
prog.template_decl({"auto... val"});
prog.declaration("static const char* const", dup + "_v", string(""));
prog.line_empty();
}
prog.comment("Template specializations\n");
for (const auto& record : alec::records) {
if (record.recipe.empty()) { // comment
std::cout << record.name << '\n';
continue;
}
std::vector<std::string> params;
params.reserve(record.args.size());
for (const auto& arg : record.args) {
params.emplace_back(arg.substr(arg.find(' ') + 1));
}
if (!record.args.empty()) {
prog.template_decl(record.args);
}
if (!record.rules.empty()) {
prog.require(join(std::begin(params),
std::end(params),
" && ",
[&](const auto& param)
{
return join(
std::begin(record.rules),
std::end(record.rules),
" && ",
[&](const auto& rule)
{ return template_def(rule + "_v", {param}); });
}));
}
const auto var = record.name + "_v";
const auto type =
dupes.contains(record.name) ? template_def(var, params) : var;
const auto* temp = !record.recipe.empty() && record.recipe[0][0] == '"'
? "details::escape_literal"
: "details::escape";
prog.declaration(
"static constexpr auto", type, template_def(temp, record.recipe));
prog.line_empty();
}
}
void generate_functions()
{
using namespace cemplate; // NOLINT
Program prog(std::cout);
prog.comment("Run-time functions\n");
for (const auto& record : alec::records) {
if (record.recipe.empty()) { // comment
std::cout << record.name << '\n';
continue;
}
std::vector<std::string> params;
params.reserve(record.args.size());
for (const auto& arg : record.args) {
params.emplace_back(arg.substr(arg.find(' ') + 1));
}
prog.function_open(record.name, "static constexpr auto", record.args);
if (!record.rules.empty()) {
for (const auto& param : params) {
prog.call(
"assert",
join(std::begin(record.rules),
std::end(record.rules),
" && ",
[&](const std::string& rule) { return call(rule, {param}); }));
}
}
if (record.args.empty()) {
prog.ret(record.name + "_v");
} else {
prog.ret(call("details::helper::make", record.recipe));
}
prog.function_close(record.name);
}
}
} // namespace
int main(int argc, char* argv[])
{
const std::span args(argv, static_cast<std::size_t>(argc));
const bool debug = argc > 1 && std::strcmp(args[1], "--debug") == 0;
std::ifstream ifile;
if (argc != 1) {
ifile.open(args[!debug ? 1 : 2]);
}
using namespace alec; // NOLINT
driver drv = argc == 1 ? driver(std::cin, debug) : driver(ifile, debug);
parser parser(drv, debug);
const int res = parser();
if (res != 0) {
std::cerr << "Parser error";
return -1;
}
// print prologue section
for (const auto& line : prologue) {
std::cout << line;
}
generate_variables();
generate_functions();
// print epilogue section
for (const auto& line : epilogue) {
std::cout << line;
}
}
diff --git a/ source/generator.h b/ source/generator.h
@@ -1,19 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace alec
{
struct record
{
std::string name;
std::vector<std::string> args;
std::vector<std::string> rules;
std::vector<std::string> recipe;
bool operator<(const record& rhs) const { return name < rhs.name; }
};
} // namespace alec
diff --git a/ source/lexer.l b/ source/lexer.l
@@ -1,83 +0,0 @@
%{
#include <stack>
#include "driver.hpp"
#include "parser.hpp"
using namespace alec;
#undef YY_DECL
#define YY_DECL int driver::yylex(parser::semantic_type* yylval, location_t *const lloc)
#define YY_USER_INIT m_yylloc = lloc;
#define YY_USER_ACTION copy_location();
std::stack<int> mode_st;
#define BEGIN_MODE(mode) do { \
if(yy_flex_debug) std::cerr<<"Starting mode: "<<mode<<std::endl; \
mode_st.push(YY_START); \
BEGIN((mode)); \
} while(0);
#define END_MODE() do { \
if(yy_flex_debug) std::cerr<<"Returning to mode: "<<mode_st.top()<<std::endl; \
BEGIN(mode_st.top()); \
mode_st.pop(); \
} while(0);
%}
%option c++ noyywrap debug nodefault
%option yyclass = "driver"
%option prefix = "yy_alec_"
LINE_END (\n|\r|\r\n)
SWITCH_BEGIN "/\*%%\*//\*"{LINE_END}
SWITCH_END "\*//\*%%\*/"{LINE_END}
%x GEN LAST
%%
%{
using Token = parser::token;
%}
{SWITCH_BEGIN} { BEGIN_MODE(GEN); return Token::SWITCH; }
.*{LINE_END} {
yylval->emplace<std::string>(yytext);
return Token::PROLOGUE;
}
<GEN>{LINE_END} { return Token::EOL; }
<GEN>^[\t ]*{LINE_END} { return Token::EOL; }
<GEN>^[\t ]*\|*[\t ]*{LINE_END} { return Token::EMPTY; }
<GEN>^[\t ]*"//".* {
yylval->emplace<std::string>(yytext);
return Token::COMMENT;
}
<GEN>, { return Token::COMMA; }
<GEN>[^,\n]* {
char *p = yytext + strlen(yytext) - 1;
while(isspace(*p)) *p-- = '\0';
while(*yytext && isspace(*yytext)) yytext++;
yylval->emplace<std::string>(yytext);
return Token::LITERAL;
}
<GEN>{SWITCH_END} {
BEGIN_MODE(LAST);
return Token::SWITCH;
}
<LAST>.*{LINE_END} {
yylval->emplace<std::string>(yytext);
return Token::EPILOGUE;
}
%%
diff --git a/ source/location.hpp b/ source/location.hpp
@@ -1,18 +0,0 @@
#pragma once
#include <cstddef>
#include <ostream>
#include <utility>
namespace alec
{
using position_t = std::size_t;
using location_t = std::pair<std::size_t, std::size_t>;
} // namespace alec
inline std::ostream& operator<<(std::ostream& ost, const alec::location_t& loc)
{
return ost << "[" << loc.first << "-" << loc.second << "]";
}
diff --git a/ source/parser.y b/ source/parser.y
@@ -1,106 +0,0 @@
%require "3.8.2"
%language "c++"
%code requires {
#include <string>
#include <cstdint>
#include "location.hpp"
#include "generator.h"
namespace alec {
class driver;
} // namespace alec
}
%define api.namespace { alec }
%define api.parser.class { parser }
%define api.value.type variant
%define api.location.type { location_t }
%locations
%define parse.error detailed
%header
%verbose
%parse-param {driver &drv}
%parse-param {const bool debug}
%initial-action
{
#if YYDEBUG != 0
set_debug_level(debug);
#endif
};
%code {
#include "driver.hpp"
namespace alec {
template<typename RHS>
void calcLocation(location_t ¤t, const RHS &rhs, const std::size_t n);
std::vector<record> records;
std::vector<std::string> epilogue;
std::vector<std::string> prologue;
} // namespace alec
#define YYLLOC_DEFAULT(Cur, Rhs, N) calcLocation(Cur, Rhs, N)
#define yylex drv.yylex
}
%left <char *> LITERAL COMMENT PROLOGUE EPILOGUE
%token EOL COMMA SWITCH EMPTY
%type <record> record
%type <std::vector<std::string>> list items
%type <std::string> name
%start document
%%
document: prologue grammar epilogue
prologue: %empty
| prologue PROLOGUE { prologue.emplace_back($2); }
;
epilogue: SWITCH
| epilogue EPILOGUE { epilogue.emplace_back($2); }
;
grammar: SWITCH
| grammar EOL
| grammar record { records.emplace_back($2); }
;
record: name list list list { $$ = record($1, $2, $3, $4); }
| COMMENT { $$ = record($1, {}, {}, {}); }
;
name: LITERAL EOL { $$ = $1; }
;
list: EMPTY { $$ = {}; }
| items EOL { $$ = $1; }
;
items: LITERAL { $$ = { $1 }; }
| items COMMA LITERAL { $1.emplace_back($3); $$ = $1; }
;
%%
namespace alec {
template<typename RHS>
inline void calcLocation(location_t ¤t, const RHS &rhs, const std::size_t n)
{
current = location_t(YYRHSLOC(rhs, 0).first, YYRHSLOC(rhs, n).second);
}
void parser::error(const location_t &location, const std::string &message)
{
std::cerr << "Error at lines " << location << ": " << message << std::endl;
}
} // namespace alec