diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt new file mode 100644 index 0000000..bf375b5 --- /dev/null +++ b/src/cpp/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) +project(mdemory) + +set(CMAKE_CXX_STANDARD 20) + +add_subdirectory(transpiler) +add_subdirectory(qtapp) + +# Add the main source files +# add_executable(MdemoryApp qtapp/main.cpp) diff --git a/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx b/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx new file mode 100644 index 0000000..363fabf Binary files /dev/null and b/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx differ diff --git a/src/cpp/qtapp/.cache/clangd/index/mocs_compilation.cpp.24E22B09DF7BB0D0.idx b/src/cpp/qtapp/.cache/clangd/index/mocs_compilation.cpp.24E22B09DF7BB0D0.idx new file mode 100644 index 0000000..2d86651 Binary files /dev/null and b/src/cpp/qtapp/.cache/clangd/index/mocs_compilation.cpp.24E22B09DF7BB0D0.idx differ diff --git a/src/cpp/qtapp/.clang-format b/src/cpp/qtapp/.clang-format new file mode 100644 index 0000000..651f916 --- /dev/null +++ b/src/cpp/qtapp/.clang-format @@ -0,0 +1,401 @@ +# clang-format +# Made by: Ingmar Delsink +# idelsink.com +# See http://clang.llvm.org/docs/ClangFormatStyleOptions.html +# Tested with: clang-format version 3.7.1 + +# General +######### + +# The style used for all options not specifically set in the configuration. +# This option is supported only in the clang-format configuration (both within -style='{...}' and the .clang-format file). +# Possible values: +# LLVM A style complying with the LLVM coding standards +# Google A style complying with Google’s C++ style guide +# Chromium A style complying with Chromium’s style guide +# Mozilla A style complying with Mozilla’s style guide +# WebKit A style complying with WebKit’s style guide +#BasedOnStyle: + +# TabWidth (unsigned) +# The number of columns used for tab stops. +TabWidth: 4 + +# IndentWidth (unsigned) +# The number of columns to use for indentation. +IndentWidth: 4 + +# UseTab (UseTabStyle) +# The way to use tab characters in the resulting file. +# Possible values: +# UT_Never (in configuration: Never) Never use tab. +# UT_ForIndentation (in configuration: ForIndentation) Use tabs only for indentation. +# UT_Always (in configuration: Always) Use tabs whenever we need to fill whitespace that spans at least from one tab stop to the next one. +UseTab: Never + +# C++ +##### + +# Language (LanguageKind) +# Language, this format style is targeted at. +# Possible values: +# LK_None (in configuration: None) Do not use. +# LK_Cpp (in configuration: Cpp) Should be used for C, C++, ObjectiveC, ObjectiveC++. +# LK_Java (in configuration: Java) Should be used for Java. +# LK_JavaScript (in configuration: JavaScript) Should be used for JavaScript. +# LK_Proto (in configuration: Proto) Should be used for Protocol Buffers (https://developers.google.com/protocol-buffers/). +# LK_TableGen (in configuration: TableGen) Should be used for TableGen code. +Language: Cpp + +# Standard (LanguageStandard) +# Format compatible with this standard, e.g. use A > instead of A> for LS_Cpp03. +# Possible values: +# LS_Cpp03 (in configuration: Cpp03) Use C++03-compatible syntax. +# LS_Cpp11 (in configuration: Cpp11) Use features of C++11 (e.g. A> instead of A >). +# LS_Auto (in configuration: Auto) Automatic detection based on the input. +Standard: Cpp11 + +# Pointer and reference alignment style. Possible values: Left, Right, Middle. +PointerAlignment: Left + +# AccessModifierOffset (int) +# The extra indent or outdent of access modifiers, e.g. public:. +AccessModifierOffset: 0 + +# AlignAfterOpenBracket (BracketAlignmentStyle) +# If true, horizontally aligns arguments after an open bracket. +# This applies to round brackets (parentheses), angle brackets and square brackets. +# Possible values: +# BAS_Align (in configuration: Align) Align parameters on the open bracket, e.g.: +# someLongFunction(argument1, +# argument2); +# BAS_DontAlign (in configuration: DontAlign) Don’t align, instead use ContinuationIndentWidth, e.g.: +# someLongFunction(argument1, +# argument2); +# BAS_AlwaysBreak (in configuration: AlwaysBreak) Always break after an open bracket, if the parameters don’t fit on a single line, e.g.: +# someLongFunction( +# argument1, argument2); +AlignAfterOpenBracket: false + +# AlignConsecutiveAssignments (bool) +# If true, aligns consecutive assignments. +# This will align the assignment operators of consecutive lines. This will result in formattings like +# int aaaa = 12; +# int b = 23; +# int ccc = 23; +AlignConsecutiveAssignments: true + +# AlignEscapedNewlinesLeft (bool) +# If true, aligns escaped newlines as far left as possible. Otherwise puts them into the right-most column. +AlignEscapedNewlinesLeft: true + +# AlignOperands (bool) +# If true, horizontally align operands of binary and ternary expressions. +# Specifically, this aligns operands of a single expression that needs to be split over multiple lines, e.g.: +# int aaa = bbbbbbbbbbbbbbb + +# ccccccccccccccc; +AlignOperands: false + +# AlignTrailingComments (bool) +# If true, aligns trailing comments. +AlignTrailingComments: true + +# AllowAllParametersOfDeclarationOnNextLine (bool) +# Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false. +AllowAllParametersOfDeclarationOnNextLine: false + +# AllowShortBlocksOnASingleLine (bool) +# Allows contracting simple braced statements to a single line. +AllowShortBlocksOnASingleLine: false + +# AllowShortCaseLabelsOnASingleLine (bool) +# If true, short case labels will be contracted to a single line. +AllowShortCaseLabelsOnASingleLine: true + +# AllowShortFunctionsOnASingleLine (ShortFunctionStyle) +# Dependent on the value, int f() { return 0; } can be put on a single line. +# Possible values: +# SFS_None (in configuration: None) Never merge functions into a single line. +# SFS_Empty (in configuration: Empty) Only merge empty functions. +# SFS_Inline (in configuration: Inline) Only merge functions defined inside a class. Implies “empty”. +# SFS_All (in configuration: All) Merge all functions fitting on a single line. +AllowShortFunctionsOnASingleLine: false + +# AllowShortIfStatementsOnASingleLine (bool) +# If true, if (a) return; can be put on a single line. +AllowShortIfStatementsOnASingleLine: false + +# AllowShortLoopsOnASingleLine (bool) +# If true, while (true) continue; can be put on a single line. +AllowShortLoopsOnASingleLine: false + +# AlwaysBreakBeforeMultilineStrings (bool) +# If true, always break before multiline string literals. +# This flag is mean to make cases where there are multiple multiline strings in a file look more consistent. Thus, it will only take effect if wrapping the string at that point leads to it being indented ContinuationIndentWidth spaces from the start of the line. +AlwaysBreakBeforeMultilineStrings: false + +# AlwaysBreakTemplateDeclarations (bool) +# If true, always break after the template<...> of a template declaration. +AlwaysBreakTemplateDeclarations: false + +# BinPackArguments (bool) +# If false, a function call’s arguments will either be all on the same line or will have one line each. +#BinPackArguments: false + +# BinPackParameters (bool) +# If false, a function declaration’s or function definition’s parameters will either all be on the same line or will have one line each. +BinPackParameters: false + +# BraceWrapping (BraceWrappingFlags) +# Control of individual brace wrapping cases. +# If BreakBeforeBraces is set to BS_Custom, use this to specify how each individual brace case should be handled. Otherwise, this is ignored. +# Nested configuration flags: +# bool AfterClass Wrap class definitions. +# bool AfterControlStatement Wrap control statements (if/for/while/switch/..). +# bool AfterEnum Wrap enum definitions. +# bool AfterFunction Wrap function definitions. +# bool AfterNamespace Wrap namespace definitions. +# bool AfterObjCDeclaration Wrap ObjC definitions (@autoreleasepool, interfaces, ..). +# bool AfterStruct Wrap struct definitions. +# bool AfterUnion Wrap union definitions. +# bool BeforeCatch Wrap before catch. +# bool BeforeElse Wrap before else. +# bool IndentBraces Indent the wrapped braces themselves. +#BraceWrapping: + +# BreakAfterJavaFieldAnnotations (bool) +# Break after each annotation on a field in Java files. +#BreakAfterJavaFieldAnnotations: + +# BreakBeforeBinaryOperators (BinaryOperatorStyle) +# The way to wrap binary operators. +# Possible values: +# BOS_None (in configuration: None) Break after operators. +# BOS_NonAssignment (in configuration: NonAssignment) Break before operators that aren’t assignments. +# BOS_All (in configuration: All) Break before operators. +BreakBeforeBinaryOperators: false + +# BreakBeforeBraces (BraceBreakingStyle) +# The brace breaking style to use. +# Possible values: +# BS_Attach (in configuration: Attach) Always attach braces to surrounding context. +# BS_Linux (in configuration: Linux) Like Attach, but break before braces on function, namespace and class definitions. +# BS_Mozilla (in configuration: Mozilla) Like Attach, but break before braces on enum, function, and record definitions. +# BS_Stroustrup (in configuration: Stroustrup) Like Attach, but break before function definitions, catch, and else. +# BS_Allman (in configuration: Allman) Always break before braces. +# BS_GNU (in configuration: GNU) Always break before braces and add an extra level of indentation to braces of control statements, not to those of class, function or other definitions. +# BS_WebKit (in configuration: WebKit) Like Attach, but break before functions. +# BS_Custom (in configuration: Custom) Configure each individual brace in BraceWrapping. +BreakBeforeBraces: Attach + +# BreakBeforeTernaryOperators (bool) +# If true, ternary operators will be placed after line breaks. +BreakBeforeTernaryOperators: false + +# BreakConstructorInitializersBeforeComma (bool) +# Always break constructor initializers before commas and align the commas with the colon. +BreakConstructorInitializersBeforeComma: false + +# BreakStringLiterals (bool) +# Allow breaking string literals when formatting. +#BreakStringLiterals: + +# ColumnLimit (unsigned) +# The column limit. +# A column limit of 0 means that there is no column limit. In this case, clang-format will respect the input’s line breaking decisions within statements unless they contradict other rules. +ColumnLimit: 80 + +# CommentPragmas (std::string) +# A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed. +CommentPragmas: '' + +# ConstructorInitializerAllOnOneLineOrOnePerLine (bool) +# If the constructor initializers don’t fit on a line, put each initializer on its own line. +ConstructorInitializerAllOnOneLineOrOnePerLine: false + +# ConstructorInitializerIndentWidth (unsigned) +# The number of characters to use for indentation of constructor initializer lists. +ConstructorInitializerIndentWidth: 0 + +# ContinuationIndentWidth (unsigned) +# Indent width for line continuations. +ContinuationIndentWidth: 0 + +# Cpp11BracedListStyle (bool) +# If true, format braced lists as best suited for C++11 braced lists. +# Important differences: - No spaces inside the braced list. - No line break before the closing brace. - Indentation with the continuation indent, not with the block indent. +# Fundamentally, C++11 braced lists are formatted exactly like function calls would be formatted in their place. If the braced list follows a name (e.g. a type or variable name), clang-format formats as if the {} were the parentheses of a function call with that name. If there is no name, a zero-length name is assumed. +Cpp11BracedListStyle: false + +# DerivePointerAlignment (bool) +# If true, analyze the formatted file for the most common alignment of & and \*. PointerAlignment is then used only as fallback. +DerivePointerBinding: false + +# DisableFormat (bool) +# Disables formatting completely. +#DisableFormat: + +# ExperimentalAutoDetectBinPacking (bool) +# If true, clang-format detects whether function calls and definitions are formatted with one parameter per line. +# Each call can be bin-packed, one-per-line or inconclusive. If it is inconclusive, e.g. completely on one line, but a decision needs to be made, clang-format analyzes whether there are other bin-packed cases in the input file and act accordingly. +# NOTE: This is an experimental flag, that might go away or be renamed. Do not use this in config files, etc. Use at your own risk. +#ExperimentalAutoDetectBinPacking: + +# ForEachMacros (std::vector) +# A vector of macros that should be interpreted as foreach loops instead of as function calls. +# These are expected to be macros of the form: +# FOREACH(, ...) +# +# In the .clang-format configuration file, this can be configured like: +# ForEachMacros: ['RANGES_FOR', 'FOREACH'] +# For example: BOOST_FOREACH. +#ForEachMacros: + +# IncludeCategories (std::vector) +# Regular expressions denoting the different #include categories used for ordering #includes. +# These regular expressions are matched against the filename of an include (including the <> or “”) in order. The value belonging to the first matching regular expression is assigned and #includes are sorted first according to increasing category number and then alphabetically within each category. +# If none of the regular expressions match, INT_MAX is assigned as category. The main header for a source file automatically gets category 0. so that it is generally kept at the beginning of the #includes (http://llvm.org/docs/CodingStandards.html#include-style). However, you can also assign negative priorities if you have certain headers that always need to be first. +# To configure this in the .clang-format file, use: +# IncludeCategories: +# - Regex: '^"(llvm|llvm-c|clang|clang-c)/' +# Priority: 2 +# - Regex: '^(<|"(gtest|isl|json)/)' +# Priority: 3 +# - Regex: '.\*' +# Priority: 1 +#IncludeCategories: + +# IndentCaseLabels (bool) +# Indent case labels one level from the switch statement. +# When false, use the same indentation level as for the switch statement. Switch statement body is always indented one level more than case labels. +IndentCaseLabels: false + +# IndentFunctionDeclarationAfterType (bool) +# If true, indent when breaking function declarations which are not also definitions after the type. +IndentFunctionDeclarationAfterType: false + +# IndentWrappedFunctionNames (bool) +# Indent if a function definition or declaration is wrapped after the type. +#IndentWrappedFunctionNames: + +# KeepEmptyLinesAtTheStartOfBlocks (bool) +# If true, empty lines at the start of blocks are kept. +#KeepEmptyLinesAtTheStartOfBlocks: + +# MacroBlockBegin (std::string) +# A regular expression matching macros that start a block. +#MacroBlockBegin: + +# MacroBlockEnd (std::string) +# A regular expression matching macros that end a block. +#MacroBlockEnd: + +# MaxEmptyLinesToKeep (unsigned) +# The maximum number of consecutive empty lines to keep. +MaxEmptyLinesToKeep: 2 + +# NamespaceIndentation (NamespaceIndentationKind) +# The indentation used for namespaces. +# Possible values: +# NI_None (in configuration: None) Don’t indent in namespaces. +# NI_Inner (in configuration: Inner) Indent only in inner namespaces (nested in other namespaces). +# NI_All (in configuration: All) Indent in all namespaces. +NamespaceIndentation: None + +# ObjCBlockIndentWidth (unsigned) +# The number of characters to use for indentation of ObjC blocks. +#ObjCBlockIndentWidth: + +# ObjCSpaceAfterProperty (bool) +# Add a space after @property in Objective-C, i.e. use @property (readonly) instead of @property(readonly). +ObjCSpaceAfterProperty: true + +# ObjCSpaceBeforeProtocolList (bool) +# Add a space in front of an Objective-C protocol list, i.e. use Foo instead of Foo. +ObjCSpaceBeforeProtocolList: true + +# PenaltyBreakBeforeFirstCallParameter (unsigned) +# The penalty for breaking a function call after call(. +PenaltyBreakBeforeFirstCallParameter: 100 + +# PenaltyBreakComment (unsigned) +# The penalty for each line break introduced inside a comment. +PenaltyBreakComment: 100 + +# PenaltyBreakFirstLessLess (unsigned) +# The penalty for breaking before the first <<. +PenaltyBreakFirstLessLess: 0 + +# PenaltyBreakString (unsigned) +# The penalty for each line break introduced inside a string literal. +PenaltyBreakString: 100 + +# PenaltyExcessCharacter (unsigned) +# The penalty for each character outside of the column limit. +PenaltyExcessCharacter: 1 + +# PenaltyReturnTypeOnItsOwnLine (unsigned) +# Penalty for putting the return type of a function onto its own line. +PenaltyReturnTypeOnItsOwnLine: 20 + +# PointerAlignment (PointerAlignmentStyle) +# Pointer and reference alignment style. +# Possible values: +# PAS_Left (in configuration: Left) Align pointer to the left. +# PAS_Right (in configuration: Right) Align pointer to the right. +# PAS_Middle (in configuration: Middle) Align pointer in the middle. +#PointerAlignment: + +# ReflowComments (bool) +# If true, clang-format will attempt to re-flow comments. +#ReflowComments: true (from v3.9) + +# SortIncludes (bool) +# If true, clang-format will sort #includes. +#SortIncludes: false (from v3.9) + +# SpaceAfterCStyleCast (bool) +# If true, a space may be inserted after C style casts. +SpaceAfterCStyleCast: false + +# SpaceBeforeAssignmentOperators (bool) +# If false, spaces will be removed before assignment operators. +SpaceBeforeAssignmentOperators: true + +# SpaceBeforeParens (SpaceBeforeParensOptions) +# Defines in which cases to put a space before opening parentheses. +# Possible values: +# SBPO_Never (in configuration: Never) Never put a space before opening parentheses. +# SBPO_ControlStatements (in configuration: ControlStatements) Put a space before opening parentheses only after control statement keywords (for/if/while...). +# SBPO_Always (in configuration: Always) Always put a space before opening parentheses, except when it’s prohibited by the syntax rules (in function-like macro definitions) or when determined by other style rules (after unary operators, opening parentheses, etc.) +SpaceBeforeParens: Always + +# SpaceInEmptyParentheses (bool) +# If true, spaces may be inserted into (). +SpaceInEmptyParentheses: false + +# SpacesBeforeTrailingComments (unsigned) +# The number of spaces before trailing line comments (// - comments). +# This does not affect trailing block comments (/* - comments) as those commonly have different usage patterns and a number of special cases. +SpacesBeforeTrailingComments: 1 + +# SpacesInAngles (bool) +# If true, spaces will be inserted after < and before > in template argument lists. +SpacesInAngles: false + +# SpacesInCStyleCastParentheses (bool) +# If true, spaces may be inserted into C style casts. +SpacesInCStyleCastParentheses: false + +# SpacesInContainerLiterals (bool) +# If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals). +SpacesInContainerLiterals: false + +# SpacesInParentheses (bool) +# If true, spaces will be inserted after ( and before ). +SpacesInParentheses: false + +# SpacesInSquareBrackets (bool) +# If true, spaces will be inserted after [ and before ]. +SpacesInSquareBrackets: false + diff --git a/src/cpp/qtapp/.gitignore b/src/cpp/qtapp/.gitignore new file mode 100644 index 0000000..0f3a6b1 --- /dev/null +++ b/src/cpp/qtapp/.gitignore @@ -0,0 +1,2 @@ +Debug +Release diff --git a/src/cpp/qtapp/CMakeLists.txt b/src/cpp/qtapp/CMakeLists.txt new file mode 100644 index 0000000..66c996f --- /dev/null +++ b/src/cpp/qtapp/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) +if(CMAKE_VERSION VERSION_LESS "3.7.0") + set(CMAKE_INCLUDE_CURRENT_DIR ON) +endif() + +project(MdemoryApp VERSION 0.1.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +find_package(Qt5 COMPONENTS Widgets REQUIRED) + +add_executable( + MdemoryApp + main.cpp +) +target_link_libraries(MdemoryApp Qt5::Widgets) diff --git a/src/cpp/qtapp/compile_commands.json b/src/cpp/qtapp/compile_commands.json new file mode 100644 index 0000000..61e3c8b --- /dev/null +++ b/src/cpp/qtapp/compile_commands.json @@ -0,0 +1,14 @@ +[ +{ + "directory": "/home/jorenchik/Code/mdemory/src/cpp/qtapp", + "command": "/usr/bin/g++ -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_WIDGETS_LIB -I/home/jorenchik/Code/mdemory/src/cpp/qtapp/MdemoryApp_autogen/include -isystem /usr/include/qt -isystem /usr/include/qt/QtWidgets -isystem /usr/include/qt/QtGui -isystem /usr/include/qt/QtCore -isystem /usr/lib/qt/mkspecs/linux-g++ -std=gnu++11 -fPIC -o CMakeFiles/MdemoryApp.dir/MdemoryApp_autogen/mocs_compilation.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/qtapp/MdemoryApp_autogen/mocs_compilation.cpp", + "file": "/home/jorenchik/Code/mdemory/src/cpp/qtapp/MdemoryApp_autogen/mocs_compilation.cpp", + "output": "CMakeFiles/MdemoryApp.dir/MdemoryApp_autogen/mocs_compilation.cpp.o" +}, +{ + "directory": "/home/jorenchik/Code/mdemory/src/cpp/qtapp", + "command": "/usr/bin/g++ -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_WIDGETS_LIB -I/home/jorenchik/Code/mdemory/src/cpp/qtapp/MdemoryApp_autogen/include -isystem /usr/include/qt -isystem /usr/include/qt/QtWidgets -isystem /usr/include/qt/QtGui -isystem /usr/include/qt/QtCore -isystem /usr/lib/qt/mkspecs/linux-g++ -std=gnu++11 -fPIC -o CMakeFiles/MdemoryApp.dir/main.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/qtapp/main.cpp", + "file": "/home/jorenchik/Code/mdemory/src/cpp/qtapp/main.cpp", + "output": "CMakeFiles/MdemoryApp.dir/main.cpp.o" +} +] \ No newline at end of file diff --git a/src/cpp/qtapp/main.cpp b/src/cpp/qtapp/main.cpp new file mode 100644 index 0000000..f7fbd90 --- /dev/null +++ b/src/cpp/qtapp/main.cpp @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Page { + int start; + int end; +}; + +class Mdem { +public: + QWidget *wMdem; + QLabel *wFrontText; + QWidget *wBack; + QVector backLabels; + QToolButton *showButton; + int labelCount; + + // Method to show or hide back labels + void showBacklabels() { + for (int i = 0; i < backLabels.size(); ++i) { + if (i < labelCount) { + if (!backLabels[i]->isVisible()) { + backLabels[i]->show(); + } + } else { + if (backLabels[i]->isVisible()) { + backLabels[i]->hide(); + } + } + } + } + + // Constructor to create an Mdem object + Mdem() { + wMdem = new QWidget(); + QVBoxLayout *vMdem = new QVBoxLayout(); + wMdem->setLayout(vMdem); + + QString id = QString("mdem_%1").arg(1); + wMdem->setObjectName(id); + + // Front + QWidget *wFront = new QWidget(); + QHBoxLayout *hFront = new QHBoxLayout(); + wFront->setMinimumHeight(60); + wFront->setLayout(hFront); + wMdem->setStyleSheet(QString( + "QWidget#%1 > QWidget {" + "border-right: 1px solid gray;" + "border-bottom: 1px solid gray;" + "border-left: 1px solid gray;" + "} " + "QWidget#%1 > QWidget[first=\"true\"] {" + "border-top: 1px solid gray;" + "}" + ).arg(id)); + + // Add Front Content + wFrontText = new QLabel(); + hFront->addWidget(wFrontText); + hFront->addStretch(1); + + showButton = new QToolButton(); + showButton->setText("Show"); + hFront->addWidget(showButton); + + // Back + wBack = new QWidget(); + QVBoxLayout *hBack = new QVBoxLayout(); + wBack->setLayout(hBack); + vMdem->addWidget(wBack); + + // Add Back Content + for (int i = 0; i < 20; ++i) { + QLabel *elBackText = new QLabel(); + hBack->addWidget(elBackText); + backLabels.append(elBackText); + } + + vMdem->addWidget(wFront); + vMdem->addWidget(wBack); + vMdem->setContentsMargins(0, 0, 0, 0); + vMdem->setSpacing(0); + + wBack->hide(); + wMdem->hide(); + + // Connect button to toggle view + QObject::connect(showButton, &QToolButton::clicked, [this]() { + if (wBack->isVisible()) { + wBack->hide(); + showButton->setText("Show"); + } else { + wBack->show(); + showButton->setText("Hide"); + } + showBacklabels(); + }); + } +}; + +QLabel *deckListLabel; +QVBoxLayout *hMdemScroll; +QSpacerItem *mdemSpacer; +QList mdems; +QString workingPath = "/home/jorenchik/Code/mdemory/memorybase"; + +void CreateMdems(QList& questions) { + if (mdemSpacer) { + hMdemScroll->removeItem(mdemSpacer); + } + + for (Mdem *mdem : mdems) { + if (mdem->wMdem->isVisible()) { + mdem->wMdem->hide(); + } + } + + // TODO +} + +void SwitchPage(int pageIdx, QList& pages, QList& mdems) { + for (Mdem *mdem : mdems) { + if (mdem->wBack->isVisible()) { + mdem->wBack->hide(); + mdem->showButton->setText("Show"); + } + } + + // TODO +} + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + QMainWindow window; + + QSplitter *hSplitter = new QSplitter(); + + // LeftSide + QWidget *leftWidget = new QWidget(); + QVBoxLayout *leftLayout = new QVBoxLayout(); + QLabel *mdemLabel = new QLabel("Mdems"); + QFileSystemModel *model = new QFileSystemModel(); + QTreeView *mdemList = new QTreeView(); + + leftWidget->setLayout(leftLayout); + leftLayout->addWidget(mdemLabel); + model->setRootPath(workingPath); + mdemList->setModel(model); + + QModelIndex rootIndex = model->index("/home/jorenchik/Code/mdemory/memorybase"); + mdemList->setRootIndex(rootIndex); + leftLayout->addWidget(mdemList); + + // DeckList + QLabel *deckLabel = new QLabel("Decks"); + QListView *deckList = new QListView(); + leftLayout->addWidget(deckLabel); + leftLayout->addWidget(deckList); + + // RightSide + QWidget *rightWidget = new QWidget(); + QVBoxLayout *rightLayout = new QVBoxLayout(); + rightWidget->setLayout(rightLayout); + + QWidget *top = new QWidget(); + QHBoxLayout *hTop = new QHBoxLayout(); + deckListLabel = new QLabel("Mdem: todo.mdem"); + top->setLayout(hTop); + rightLayout->addWidget(top); + + hTop->addWidget(deckListLabel); + hTop->addStretch(1); + + QToolButton *refresh = new QToolButton(); + QToolButton *practice = new QToolButton(); + QToolButton *shuffle = new QToolButton(); + + hTop->addWidget(refresh); + hTop->addWidget(shuffle); + hTop->addWidget(practice); + + // Buttons + refresh->setText("Refresh"); + shuffle->setText("Shuffle"); + practice->setText("Practice"); + + // Mdems + QScrollArea *mdemScroll = new QScrollArea(); + QWidget *mdemContainer = new QWidget(); + hMdemScroll = new QVBoxLayout(); + mdemScroll->setWidget(mdemContainer); + mdemScroll->setWidgetResizable(true); + mdemContainer->setLayout(hMdemScroll); + rightLayout->addWidget(mdemScroll); + + // Pagination + hSplitter->addWidget(leftWidget); + hSplitter->addWidget(rightWidget); + hSplitter->setStretchFactor(0, 1); + hSplitter->setStretchFactor(1, 3); + + window.setCentralWidget(hSplitter); + window.show(); + return app.exec(); +} diff --git a/src/cpp/transpiler/.cache/clangd/index/api.h.09188030E1E1AD2B.idx b/src/cpp/transpiler/.cache/clangd/index/api.h.09188030E1E1AD2B.idx new file mode 100644 index 0000000..3babb38 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/api.h.09188030E1E1AD2B.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/config.h.37161B9BC74F84AB.idx b/src/cpp/transpiler/.cache/clangd/index/config.h.37161B9BC74F84AB.idx new file mode 100644 index 0000000..cbe0f87 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/config.h.37161B9BC74F84AB.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/lexer.cpp.83041CDDE7BB82EA.idx b/src/cpp/transpiler/.cache/clangd/index/lexer.cpp.83041CDDE7BB82EA.idx new file mode 100644 index 0000000..a4c1a2a Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/lexer.cpp.83041CDDE7BB82EA.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/lexer.h.56AED8998E88A18A.idx b/src/cpp/transpiler/.cache/clangd/index/lexer.h.56AED8998E88A18A.idx new file mode 100644 index 0000000..75fe5a4 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/lexer.h.56AED8998E88A18A.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx b/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx new file mode 100644 index 0000000..38c0024 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx b/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx new file mode 100644 index 0000000..6231dc1 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/parser.h.A6B071E84CF21EB1.idx b/src/cpp/transpiler/.cache/clangd/index/parser.h.A6B071E84CF21EB1.idx new file mode 100644 index 0000000..903df12 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/parser.h.A6B071E84CF21EB1.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/result.h.9FDF8FE2A27152FF.idx b/src/cpp/transpiler/.cache/clangd/index/result.h.9FDF8FE2A27152FF.idx new file mode 100644 index 0000000..ff249d2 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/result.h.9FDF8FE2A27152FF.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/time.h.A5DECF25CDA82FFC.idx b/src/cpp/transpiler/.cache/clangd/index/time.h.A5DECF25CDA82FFC.idx new file mode 100644 index 0000000..96863a0 Binary files /dev/null and b/src/cpp/transpiler/.cache/clangd/index/time.h.A5DECF25CDA82FFC.idx differ diff --git a/src/cpp/transpiler/.gitignore b/src/cpp/transpiler/.gitignore new file mode 100644 index 0000000..0f3a6b1 --- /dev/null +++ b/src/cpp/transpiler/.gitignore @@ -0,0 +1,2 @@ +Debug +Release diff --git a/src/cpp/transpiler/.vscode/launch.json b/src/cpp/transpiler/.vscode/launch.json new file mode 100644 index 0000000..54d9930 --- /dev/null +++ b/src/cpp/transpiler/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "name": "C/C++: gcc.exe build and debug active file", + "request": "launch", + "program": "./Debug/transpiler", + "args": ["input.mdem"], + "cwd": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/src/cpp/transpiler/.vscode/settings.json b/src/cpp/transpiler/.vscode/settings.json new file mode 100644 index 0000000..d12d5bd --- /dev/null +++ b/src/cpp/transpiler/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cmake.configureOnOpen": true, + "cmake.generator": "Unix Makefiles", // might use Ninja too + "cmake.buildDirectory": "${workspaceFolder}/build" +} diff --git a/src/cpp/transpiler/.vscode/tasks.json b/src/cpp/transpiler/.vscode/tasks.json new file mode 100644 index 0000000..182561b --- /dev/null +++ b/src/cpp/transpiler/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + "tasks": [ + { + "label": "Build transpiler", + "command": "cmake", + "args": [ + "--build Debug", + ], + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/src/cpp/transpiler/CMakeLists.txt b/src/cpp/transpiler/CMakeLists.txt new file mode 100644 index 0000000..ed250ef --- /dev/null +++ b/src/cpp/transpiler/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.10) +project(MemoryTranspiler LANGUAGES CXX) + +# C++ standard spec +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Give info for intellisense +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Set sources +set(SOURCES + main.cpp + lexer.cpp + parser.cpp + time.cpp + api.cpp +) +include_directories(${CMAKE_SOURCE_DIR}/include) + +# Add the executable (CLI) +add_executable(transpiler ${SOURCES}) +target_compile_options(transpiler PRIVATE -Wall -Wextra -Wpedantic) diff --git a/src/cpp/transpiler/api.cpp b/src/cpp/transpiler/api.cpp new file mode 100644 index 0000000..bbcc7c1 --- /dev/null +++ b/src/cpp/transpiler/api.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include "api.h" +#include "result.h" +#include "lexer.h" +#include "parser.h" +#include "time.h" + +Result> Transpile(std::string fileContent) { + start = std::chrono::high_resolution_clock::now(); + auto lexRes = TokenizeMdem(fileContent); + auto tokens = lexRes.value; + if (lexRes.error.length() > 0) { + return { + {}, + std::format( + "Lexical analysis error: {}", + lexRes.error + ), + lexRes.row, + lexRes.column + }; + } + + auto parseRes = ParseQuestions(tokens); + auto questions = parseRes.value; + if (parseRes.error.length() > 0) { + return { + {}, + std::format( + "Parsing error: {}", + parseRes.error + ), + parseRes.row, + parseRes.column + }; + } + + end = std::chrono::high_resolution_clock::now(); + ShowTime("Transpilation time"); + return {questions}; +} diff --git a/src/cpp/transpiler/api.h b/src/cpp/transpiler/api.h new file mode 100644 index 0000000..d5c75c1 --- /dev/null +++ b/src/cpp/transpiler/api.h @@ -0,0 +1,4 @@ +#include "result.h" +#include "parser.h" + +Result> Transpile(std::string fileContent); diff --git a/src/cpp/transpiler/compile_commands.json b/src/cpp/transpiler/compile_commands.json new file mode 100644 index 0000000..8e57e25 --- /dev/null +++ b/src/cpp/transpiler/compile_commands.json @@ -0,0 +1,20 @@ +[ +{ + "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/Debug", + "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/transpiler/include -g -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/main.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/main.cpp", + "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/main.cpp", + "output": "CMakeFiles/transpiler.dir/main.cpp.o" +}, +{ + "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/Debug", + "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/transpiler/include -g -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/lexer.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/lexer.cpp", + "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/lexer.cpp", + "output": "CMakeFiles/transpiler.dir/lexer.cpp.o" +}, +{ + "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/Debug", + "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/transpiler/include -g -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/parser.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/parser.cpp", + "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/parser.cpp", + "output": "CMakeFiles/transpiler.dir/parser.cpp.o" +} +] \ No newline at end of file diff --git a/src/cpp/transpiler/config.h b/src/cpp/transpiler/config.h new file mode 100644 index 0000000..58f8154 --- /dev/null +++ b/src/cpp/transpiler/config.h @@ -0,0 +1,4 @@ +#pragma once + +extern bool debug; + diff --git a/src/cpp/transpiler/lexer.cpp b/src/cpp/transpiler/lexer.cpp new file mode 100644 index 0000000..81446fd --- /dev/null +++ b/src/cpp/transpiler/lexer.cpp @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lexer.h" +#include "config.h" +#include "result.h" + +std::vector tokens; +std::vector buffer; +int32_t row; +int32_t column; +int32_t previousRow; +int32_t previousColumn; +bool textStarted = false; +bool identifierStarted = false; + +void trimString(std::string &str, std::string trimChars) { + int padSize = 0; + bool pad = false; + for (size_t i = 0; i < str.size(); ++i) { + for (size_t k = 0; k < trimChars.size(); ++k) { + if (str[i] == trimChars[k]) { + padSize++; + pad = true; + break; + } + } + if (!pad) { + break; + } + pad = false; + } + if (padSize > 0) { + str.erase(0, padSize); + } + padSize = 0; + pad = false; + for (size_t i = str.size(); i-- > 0;) { + for (size_t k = 0; k < trimChars.size(); ++k) { + if (str[i] == trimChars[k]) { + padSize++; + pad = true; + break; + } + } + if (!pad) { + break; + } + pad = false; + } + if (padSize > 0) { + str.erase(str.end() - padSize, str.end()); + } +} + +void makeTokenWithTokenBuffer( + TokenType ttype, + size_t tokenLen, + TokenType textType +) { + std::string token(buffer.end() - tokenLen, buffer.end()); + if (buffer.size() > tokenLen) { + std::string prevFragment(buffer.begin(), buffer.end() - tokenLen); + trimString(prevFragment, " \n\t"); + if (prevFragment.length() > 0) { + tokens.push_back(Token{ + textType, + prevFragment, + previousRow, + previousColumn + }); + } + } + buffer.clear(); + + tokens.push_back(Token{ + ttype, + token, + row, + column + }); + + previousRow = row; + previousColumn = column; + buffer.clear(); +} + +Result> TokenizeMdem(const std::string& fileRunes) { + row = 1; + column = 1; + previousRow = 1; + previousColumn = 1; + textStarted = false; + tokens.clear(); + buffer.clear(); + + if (fileRunes.find_first_not_of(" \n\t") == std::string::npos) { + return {tokens, ""}; + } + + for (size_t i = 0; i < fileRunes.size(); ++i) { + char c = fileRunes[i]; + + // AdvancePointer + if (c == '\n') { + row += 1; + column = 0; + } + buffer.push_back(c); + + // SkipWhitetext + if (!textStarted) { + if (c == '\n') { + previousRow += 1; + previousColumn = 1; + } else if (c == ' ') { + previousColumn += 1; + } else if (c == '\t') { + previousColumn += 4; + } else { + textStarted = true; + } + } + + // EmitTokens + switch (c) { + case '[': + makeTokenWithTokenBuffer( + TokenType::IdentifierStart, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + textStarted = false; + identifierStarted = true; + break; + case ']': + if (!identifierStarted) { + return { + tokens, + "Cannot end identifier if it is not started", + tokens[i].row, + tokens[i].column + }; + } + makeTokenWithTokenBuffer( + TokenType::IdentifierEnd, + 1, + TokenType::Identifier + ); + previousRow = row; + previousColumn = column; + textStarted = false; + identifierStarted = false; + break; + case '#': + makeTokenWithTokenBuffer( + TokenType::SectionIdentifierStart, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + textStarted = false; + break; + case '{': + makeTokenWithTokenBuffer( + TokenType::SectionStart, + 1, + TokenType::Identifier + ); + previousRow = row; + previousColumn = column; + textStarted = false; + break; + case '}': + makeTokenWithTokenBuffer( + TokenType::SectionEnd, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + textStarted = false; + break; + case '-': + makeTokenWithTokenBuffer( + TokenType::ElementDashStart, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + textStarted = false; + break; + case '>': + makeTokenWithTokenBuffer( + TokenType::QuestionEnd, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + break; + case '+': + makeTokenWithTokenBuffer( + TokenType::ElementPlusStart, + 1, + TokenType::TextFragment + ); + previousRow = row; + previousColumn = column; + textStarted = false; + break; + } + + column += 1; + } + + makeTokenWithTokenBuffer( + TokenType::EndOfFile, + 0, + TokenType::TextFragment + ); + + if (debug) { + std::cout << "SECTION: Lexer output:\n"; + std::cout << std::format("Token count: {}", tokens.size()) << std::endl; + for (const Token& token : tokens) { + std::cout << token.ToString(); + } + std::cout << "SECTION END: Lexer output\n"; + } + + return {tokens, ""}; +} + +std::regex nextLineExp( + "\n", + std::regex_constants::ECMAScript +); + +std::regex doubleSpaceExp( + "\\s\\s+", + std::regex_constants::ECMAScript +); + +std::string Token::ToString(const TokenType* ttype) { + switch (*ttype) { + case TokenType::TextFragment: return "text fragment"; + case TokenType::QuestionEnd: return "question end symbol"; + case TokenType::ElementDashStart: return "dash element start"; + case TokenType::ElementPlusStart: return "plus element start"; + case TokenType::Identifier: return "identifier"; + case TokenType::IdentifierStart: return "start of identifier"; + case TokenType::IdentifierEnd: return "end of identifier"; + case TokenType::SectionIdentifierStart: return "section identifier start"; + case TokenType::SectionStart: return "start of section"; + case TokenType::SectionEnd: return "end of section"; + case TokenType::EndOfFile: return "end of file"; + default: return "unrecognized token"; + } +} + +std::string Token::ToString() const { + std::string contentStr = content; + if (tokenType == TokenType::TextFragment) { + contentStr = std::regex_replace(contentStr, nextLineExp, ""); + contentStr = std::regex_replace(contentStr, doubleSpaceExp, " "); + } + return std::format( + "{}: \"{}\" ({}:{})\n", + ToString(&tokenType), + contentStr, + row, + column + ); +} diff --git a/src/cpp/transpiler/lexer.h b/src/cpp/transpiler/lexer.h new file mode 100644 index 0000000..10cebb1 --- /dev/null +++ b/src/cpp/transpiler/lexer.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include "result.h" + +enum class TokenType { + TextFragment, + QuestionEnd, + ElementDashStart, + ElementPlusStart, + Identifier, + IdentifierStart, + IdentifierEnd, + SectionIdentifierStart, + SectionStart, + SectionEnd, + SOF, + EndOfFile +}; + +struct Token { + TokenType tokenType; + std::string content; + int32_t row; + int32_t column; + + std::string ToString() const; + static std::string ToString(const TokenType* ttype); +}; + +Result> TokenizeMdem(const std::string& fileRunes); diff --git a/src/cpp/transpiler/main.cpp b/src/cpp/transpiler/main.cpp new file mode 100644 index 0000000..424cd6b --- /dev/null +++ b/src/cpp/transpiler/main.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "api.h" +#include "parser.h" +#include "time.h" +#include "config.h" + +std::string readFile(const std::string& filePath) { + std::ifstream file(filePath); + if (!file.is_open()) { + throw std::runtime_error("Cannot open file: " + filePath); + } + + std::string content; + std::string line; + while (std::getline(file, line)) { + content += line + '\n'; + } + + file.close(); + return content; +} + +bool debug = false; + +std::chrono::high_resolution_clock::time_point start; +std::chrono::high_resolution_clock::time_point end; + +int main(int argc, char* argv[]) { + std::string filePath; + + if (argc == 3) { + auto option = std::string(argv[1]); + if (option == "--debug") { + debug = true; + } else { + std::cout << std::format("Unrecognized option: {}", option) << std::endl; + return 1; + } + filePath = argv[2]; + } else if (argc == 2) { + filePath = argv[1]; + } else { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + try { + start = std::chrono::high_resolution_clock::now(); + std::string fileContent = readFile(filePath); + end = std::chrono::high_resolution_clock::now(); + ShowTime("I/O time"); + + auto res = Transpile(fileContent); + auto questions = res.value; + if (res.error.length() > 0) { + std::cout << std::format( + "{} ({}:{})\n", + res.error, + res.row, + res.column + ); + return 1; + } + + for (Question* question: questions) { + delete question; + } + + } catch (std::exception &e) { + std::cout << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/cpp/transpiler/parser.cpp b/src/cpp/transpiler/parser.cpp new file mode 100644 index 0000000..0ab7607 --- /dev/null +++ b/src/cpp/transpiler/parser.cpp @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "lexer.h" +#include "result.h" +#include "parser.h" + +struct QuestionElement { + bool isDash; + std::string content; +}; + +std::string SingleAnswerQuestion::ToString() const { + return std::format( + ":{} section:{} id:{} answer:{}", + QuestionText, + Section, + ID, + Answer + ); +} + +std::string MultipleChoiceQuestion::ToString() const { + std::stringstream choiceOut; + for (const auto& choice : Choices) { + char opener; + if (choice.IsCorrect) { + opener = '+'; + } else { + opener = '-'; + } + choiceOut << opener << " " << choice.Answer << "; "; + } + return std::format( + ":{} section: {} id: {}\n{}", + QuestionText, + Section, + ID, + choiceOut.str() + ); +} + +// Automaton for validating token transitions +std::map> automata; + +bool contains(const std::vector& vec, TokenType element) { + return std::find(vec.begin(), vec.end(), element) != vec.end(); +} + +// Automata for validating the parser state +std::map> parserAutomata() { + std::map> automata; + automata[TokenType::TextFragment] = { + TokenType::QuestionEnd, + TokenType::ElementDashStart, + TokenType::ElementPlusStart, + TokenType::SectionIdentifierStart, + TokenType::SectionStart, + TokenType::EndOfFile, + TokenType::SectionEnd + }; + automata[TokenType::QuestionEnd] = { + TokenType::ElementDashStart, + TokenType::ElementPlusStart + }; + automata[TokenType::ElementDashStart] = { + TokenType::IdentifierStart, + TokenType::TextFragment + }; + automata[TokenType::ElementPlusStart] = { + TokenType::TextFragment + }; + automata[TokenType::Identifier] = { + TokenType::IdentifierEnd, + TokenType::SectionStart + }; + automata[TokenType::IdentifierStart] = { + TokenType::Identifier + }; + automata[TokenType::IdentifierEnd] = { + TokenType::TextFragment + }; + automata[TokenType::SectionIdentifierStart] = { + TokenType::Identifier + }; + automata[TokenType::SectionStart] = { + TokenType::ElementDashStart, + TokenType::SectionIdentifierStart, + TokenType::EndOfFile + }; + automata[TokenType::SectionEnd] = { + TokenType::SectionIdentifierStart, + TokenType::ElementDashStart, + TokenType::EndOfFile + }; + automata[TokenType::SOF] = { + TokenType::ElementDashStart, + TokenType::SectionIdentifierStart, + TokenType::EndOfFile + }; + automata[TokenType::EndOfFile] = {}; + return automata; +} + +std::string capitalize(const std::string& str) { + if (str.empty()) return str; + std::string result = str; + result[0] = std::towupper(result[0]); + return result; +} + +Result ValidateGrammar(const std::vector& tokens) { + automata = parserAutomata(); + for (size_t i = 0; i < tokens.size() - 1; ++i) { + Token token = tokens[i]; + Token nextToken = tokens[i + 1]; + if (!contains(automata[token.tokenType], nextToken.tokenType)) { + return { + .error=std::format( + "Invalid token sequence: {} cannot precede {}", + std::string(capitalize(Token::ToString(&token.tokenType))), + std::string(capitalize(Token::ToString(&nextToken.tokenType))) + ), + .row=token.row, + .column=token.column + }; + } + } + return {}; +} + +Result> ParseQuestions(const std::vector& tokens) { + auto questions = std::vector(); + + auto result = ValidateGrammar(tokens); + if (result.error.length() > 0) { + return { + questions, + result.error, + result.row, + result.column + }; + } + + std::string section; + size_t i = 0; + + if (debug) { + std::cout << "SECTION: Parser output:\n"; + } + while (i < tokens.size()) { + if (tokens[i].tokenType == TokenType::ElementDashStart) { + + std::string id, questionText; + std::vector questionElements; + // Parsing for a single question or multiple choice question + if (tokens[i + 1].tokenType == TokenType::IdentifierStart) { + id = tokens[i + 2].content; + questionText = tokens[i + 4].content; + i += 6; + } else { + id = ""; + questionText = tokens[i + 1].content; + i += 3; + } + + while (true) { + if (i + 3 < tokens.size() && tokens[i + 3].tokenType != TokenType::EndOfFile) { + size_t offset = tokens[i + 1].tokenType == TokenType::IdentifierStart ? 5 : 2; + if (tokens[i].tokenType == TokenType::SectionIdentifierStart || + tokens[i].tokenType == TokenType::SectionEnd) { + break; + } + if (i + offset < tokens.size() && tokens[i + offset].tokenType == TokenType::QuestionEnd) { + break; + } + if (offset == 5 && tokens[i + 5].tokenType != TokenType::QuestionEnd) { + return { + questions, + "Invalid identifier placement", + tokens[i].row, + tokens[i].column + }; + } + } + if (i + 2 >= tokens.size()) { + break; + } + + // Create question elements + QuestionElement questionElement; + questionElement.isDash = (tokens[i].tokenType == TokenType::ElementDashStart); + questionElement.content = tokens[i + 1].content; + questionElements.push_back(questionElement); + i += 2; + } + + if (questionElements.size() > 1) { + auto* mcq = new MultipleChoiceQuestion(); + mcq->ID = id; + mcq->QuestionText = questionText; + for (const auto& elem : questionElements) { + Choice choice; + choice.Answer = elem.content; + choice.IsCorrect = !elem.isDash; + mcq->Choices.push_back(choice); + } + mcq->Section = section; + questions.push_back(mcq); + if (debug) { + std::cout << mcq->ToString() << "\n"; + } + } else if (questionElements.size() == 1) { + auto* saq = new SingleAnswerQuestion(); + saq->ID = id; + saq->QuestionText = questionText; + saq->Answer = questionElements[0].content; + saq->Section = section; + questions.push_back(saq); + if (debug) { + std::cout << saq->ToString() << "\n"; + } + } + } else if (tokens[i].tokenType == TokenType::SectionIdentifierStart) { + if (section.length() > 0) { + return { + questions, + std::format("Section \"{}\" is not closed, cannot open other section", section), + tokens[i].row, + tokens[i].column + }; + } + section = tokens[i + 1].content; + i += 3; + if (debug) { + std::cout << "Started section: " << section << "\n"; + } + } else if (tokens[i].tokenType == TokenType::SectionEnd) { + if (section.length() <= 0) { + return { + questions, + std::format("Cannot close section, no section was opened", section), + tokens[i].row, + tokens[i].column + }; + } + section.clear(); + i += 1; + if (debug) { + std::cout << "Section ended.\n"; + } + } else if (tokens[i].tokenType == TokenType::EndOfFile) { + if (debug) { + std::cout << "File terminated: EndOfFile\n"; + } + break; + } else { + return { + questions, + "Unexpected token encountered", + tokens[i].row, + tokens[i].column + }; + } + } + + if (debug) { + std::cout << "SECTION END: Parser output:\n"; + } + return {questions, ""}; +} diff --git a/src/cpp/transpiler/parser.h b/src/cpp/transpiler/parser.h new file mode 100644 index 0000000..022afec --- /dev/null +++ b/src/cpp/transpiler/parser.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include "lexer.h" +#include "result.h" + +class Question { +public: + virtual std::string ToString() const = 0; + virtual ~Question() = default; +}; + +// Choice struct for Multiple Choice Questions +struct Choice { + std::string Answer; + bool IsCorrect; +}; + +class SingleAnswerQuestion : public Question { +public: + std::string ID; + std::string QuestionText; + std::string Answer; + std::string Section; + + // Override ToString method + std::string ToString() const override; +}; + +class MultipleChoiceQuestion : public Question { +public: + std::string ID; + std::string QuestionText; + std::vector Choices; + std::string Section; + + // Override ToString method + std::string ToString() const override; +}; + +Result> ParseQuestions(const std::vector& tokens); diff --git a/src/cpp/transpiler/result.h b/src/cpp/transpiler/result.h new file mode 100644 index 0000000..06cdf0c --- /dev/null +++ b/src/cpp/transpiler/result.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +struct NoneType {}; + +template +struct Result { + T value; + std::string error = ""; + int row = -1; + int column = -1; +}; diff --git a/src/cpp/transpiler/time.cpp b/src/cpp/transpiler/time.cpp new file mode 100644 index 0000000..bdcc551 --- /dev/null +++ b/src/cpp/transpiler/time.cpp @@ -0,0 +1,10 @@ +#include +#include + +#include "time.h" + +void ShowTime(std::string label) { + auto duration = std::chrono::duration_cast(end - start); + double_t msDuration = (double_t) duration.count() / 1000; + std::cout << std::format("{}: {:.3f} ms", label, msDuration) << std::endl; +} diff --git a/src/cpp/transpiler/time.h b/src/cpp/transpiler/time.h new file mode 100644 index 0000000..9bfbd75 --- /dev/null +++ b/src/cpp/transpiler/time.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +extern std::chrono::high_resolution_clock::time_point start; +extern std::chrono::high_resolution_clock::time_point end; + +void ShowTime(std::string label); diff --git a/src/compiler/.gitignore b/src/go/compiler/.gitignore similarity index 100% rename from src/compiler/.gitignore rename to src/go/compiler/.gitignore diff --git a/src/compiler/api/api.go b/src/go/compiler/api/api.go similarity index 100% rename from src/compiler/api/api.go rename to src/go/compiler/api/api.go diff --git a/src/compiler/comperror/comperror.go b/src/go/compiler/comperror/comperror.go similarity index 100% rename from src/compiler/comperror/comperror.go rename to src/go/compiler/comperror/comperror.go diff --git a/src/compiler/compiler.go b/src/go/compiler/compiler.go similarity index 100% rename from src/compiler/compiler.go rename to src/go/compiler/compiler.go diff --git a/src/compiler/deck.moml b/src/go/compiler/deck.moml similarity index 100% rename from src/compiler/deck.moml rename to src/go/compiler/deck.moml diff --git a/src/compiler/go.mod b/src/go/compiler/go.mod similarity index 100% rename from src/compiler/go.mod rename to src/go/compiler/go.mod diff --git a/src/compiler/go.sum b/src/go/compiler/go.sum similarity index 100% rename from src/compiler/go.sum rename to src/go/compiler/go.sum diff --git a/src/compiler/lexer/lexer.go b/src/go/compiler/lexer/lexer.go similarity index 100% rename from src/compiler/lexer/lexer.go rename to src/go/compiler/lexer/lexer.go diff --git a/src/compiler/parser/parser.go b/src/go/compiler/parser/parser.go similarity index 100% rename from src/compiler/parser/parser.go rename to src/go/compiler/parser/parser.go diff --git a/src/mdemory-app-qt/.gitignore b/src/go/mdemory-app-qt/.gitignore similarity index 100% rename from src/mdemory-app-qt/.gitignore rename to src/go/mdemory-app-qt/.gitignore diff --git a/src/mdemory-app-qt/.vscode/launch.json b/src/go/mdemory-app-qt/.vscode/launch.json similarity index 100% rename from src/mdemory-app-qt/.vscode/launch.json rename to src/go/mdemory-app-qt/.vscode/launch.json diff --git a/src/mdemory-app-qt/go.mod b/src/go/mdemory-app-qt/go.mod similarity index 100% rename from src/mdemory-app-qt/go.mod rename to src/go/mdemory-app-qt/go.mod diff --git a/src/mdemory-app-qt/go.sum b/src/go/mdemory-app-qt/go.sum similarity index 100% rename from src/mdemory-app-qt/go.sum rename to src/go/mdemory-app-qt/go.sum diff --git a/src/mdemory-app-qt/main b/src/go/mdemory-app-qt/main similarity index 100% rename from src/mdemory-app-qt/main rename to src/go/mdemory-app-qt/main diff --git a/src/mdemory-app-qt/main.go b/src/go/mdemory-app-qt/main.go similarity index 100% rename from src/mdemory-app-qt/main.go rename to src/go/mdemory-app-qt/main.go