From fa2d69de2b662a88e784584bbf456241f67731ea Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Thu, 6 Oct 2022 00:33:37 -0300 Subject: [PATCH 1/3] ignore vscode and meson build directories --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 4d67604..ed3680d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.code-workspace +.vscode +builddir From 950efa945e6916e0395fce50e9475f43740782bd Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 17 Oct 2022 22:26:36 -0300 Subject: [PATCH 2/3] add lexer, parser, and ast tree --- .clang-format | 220 +++++++++++++++++++++++++++++ .pre-commit-config.yaml | 33 +++++ compile_commands.json | 20 +++ meson.build | 11 ++ src/ast/call_expression_ast.h | 34 +++++ src/ast/expression_ast.h | 13 ++ src/ast/function_ast.h | 30 ++++ src/ast/meson.build | 8 ++ src/ast/number_expression_ast.h | 24 ++++ src/ast/prototype_ast.h | 34 +++++ src/ast/variable_expression_ast.h | 25 ++++ src/lexer.cpp | 121 ++++++++++++++++ src/lexer.h | 46 ++++++ src/meson.build | 13 ++ src/obelisk.cpp | 50 +++++++ src/obelisk.h | 1 + src/parser.cpp | 225 ++++++++++++++++++++++++++++++ src/parser.h | 53 +++++++ 18 files changed, 961 insertions(+) create mode 100644 .clang-format create mode 100644 .pre-commit-config.yaml create mode 100644 compile_commands.json create mode 100644 meson.build create mode 100644 src/ast/call_expression_ast.h create mode 100644 src/ast/expression_ast.h create mode 100644 src/ast/function_ast.h create mode 100644 src/ast/meson.build create mode 100644 src/ast/number_expression_ast.h create mode 100644 src/ast/prototype_ast.h create mode 100644 src/ast/variable_expression_ast.h create mode 100644 src/lexer.cpp create mode 100644 src/lexer.h create mode 100644 src/meson.build create mode 100644 src/obelisk.cpp create mode 100644 src/obelisk.h create mode 100644 src/parser.cpp create mode 100644 src/parser.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..378674a --- /dev/null +++ b/.clang-format @@ -0,0 +1,220 @@ +--- +AccessModifierOffset: 0 +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always +IndentAccessModifiers: true +TabWidth: 4 +UseTab: Never +UseCRLF: false +Language: Cpp +IndentWidth: 4 +AlignAfterOpenBracket: DontAlign +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: AcrossComments +AlignConsecutiveBitFields: AcrossComments +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: AcrossComments +AlignEscapedNewlines: Left +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +BinPackArguments: false +BinPackParameters: false +AttributeMacros: + - __capability +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: All +BreakBeforeConceptDeclarations: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: ^ + Priority: 2 + CaseSensitive: false + SortPriority: 0 + - Regex: ^<.*\.h> + Priority: 1 + CaseSensitive: false + SortPriority: 0 + - Regex: ^<.* + Priority: 2 + CaseSensitive: false + SortPriority: 0 + - Regex: .* + Priority: 0 + CaseSensitive: false + SortPriority: 0 +IncludeIsMainRegex: ([-_](test|unittest))?$ +IncludeIsMainSourceRegex: "" +IndentCaseBlocks: true +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: false +IndentPPDirectives: BeforeHash +IndentRequires: true +IndentWrappedFunctionNames: true +InsertTrailingCommas: None +JavaScriptQuotes: Double +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: OuterScope +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: false +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: 4 +PackConstructorInitializers: Never +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +QualifierAlignment: Left +RawStringFormats: + - Language: Cpp + BasedOnStyle: google + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - c++ + - C++ + CanonicalDelimiter: "" + - Language: TextProto + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + BasedOnStyle: google + Delimiters: + - pb + - PB + - proto + - PROTO + CanonicalDelimiter: pb +ReferenceAlignment: Left +ReflowComments: false +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 0 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: false + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: false + AfterOverloadedOperator: true + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4a1c397 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-case-conflict + - id: mixed-line-ending + - id: check-added-large-files + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: detect-private-key + - id: no-commit-to-branch + args: [--branch, master, --branch, develop] + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v14.0.6 + hooks: + - id: clang-format + types_or: [c++, c] + args: ["-style=file"] + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.3.1 + hooks: + - id: forbid-crlf + - id: forbid-tabs +#- repo: https://github.com/pocc/pre-commit-hooks +# rev: v1.3.5 +# hooks: +# - id: clang-tidy +# args: [-checks=clang-diagnostic-return-type] +# files: src/.*\.cpp diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..70b729b --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,20 @@ +[ + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "clang++ -Isrc/obelisk.p -Isrc -I../src -fcolor-diagnostics -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -O0 -g -MD -MQ src/obelisk.p/obelisk.cpp.o -MF src/obelisk.p/obelisk.cpp.o.d -o src/obelisk.p/obelisk.cpp.o -c ../src/obelisk.cpp", + "file": "../src/obelisk.cpp", + "output": "src/obelisk.p/obelisk.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "clang++ -Isrc/obelisk.p -Isrc -I../src -fcolor-diagnostics -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -O0 -g -MD -MQ src/obelisk.p/lexer.cpp.o -MF src/obelisk.p/lexer.cpp.o.d -o src/obelisk.p/lexer.cpp.o -c ../src/lexer.cpp", + "file": "../src/lexer.cpp", + "output": "src/obelisk.p/lexer.cpp.o" + }, + { + "directory": "/mnt/data/ubb/courses/proyecto-titulo/obelisk/builddir", + "command": "clang++ -Isrc/obelisk.p -Isrc -I../src -fcolor-diagnostics -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -O0 -g -MD -MQ src/obelisk.p/parser.cpp.o -MF src/obelisk.p/parser.cpp.o.d -o src/obelisk.p/parser.cpp.o -c ../src/parser.cpp", + "file": "../src/parser.cpp", + "output": "src/obelisk.p/parser.cpp.o" + } +] diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..7b2deae --- /dev/null +++ b/meson.build @@ -0,0 +1,11 @@ +project('obelisk', + 'cpp', + version : '1.0.0', + default_options : [ + 'warning_level=3', + 'c_std=c17', + 'cpp_std=c++17' + ] +) + +subdir('src') diff --git a/src/ast/call_expression_ast.h b/src/ast/call_expression_ast.h new file mode 100644 index 0000000..247e0fb --- /dev/null +++ b/src/ast/call_expression_ast.h @@ -0,0 +1,34 @@ +#ifndef OBELISK_AST_CALL_EXPRESSION_AST_H +#define OBELISK_AST_CALL_EXPRESSION_AST_H + +#include "ast/expression_ast.h" + +#include +#include +#include + +namespace obelisk +{ + class CallExpressionAST : public ExpressionAST + { + private: + std::string callee_; + std::vector> args_; + + std::string getCallee(); + void setCallee(std::string callee); + + std::vector> getArgs(); + void setArgs(std::vector> args); + + public: + CallExpressionAST(const std::string &callee, + std::vector> args) : + callee_(callee), + args_(std::move(args)) + { + } + }; +} // namespace obelisk + +#endif diff --git a/src/ast/expression_ast.h b/src/ast/expression_ast.h new file mode 100644 index 0000000..11ba2d9 --- /dev/null +++ b/src/ast/expression_ast.h @@ -0,0 +1,13 @@ +#ifndef OBELISK_AST_EXPRESSION_AST_H +#define OBELISK_AST_EXPRESSION_AST_H + +namespace obelisk +{ + class ExpressionAST + { + public: + virtual ~ExpressionAST() = default; + }; +} // namespace obelisk + +#endif diff --git a/src/ast/function_ast.h b/src/ast/function_ast.h new file mode 100644 index 0000000..1d4ff4a --- /dev/null +++ b/src/ast/function_ast.h @@ -0,0 +1,30 @@ +#ifndef OBELISK_AST_FUNCTION_AST_H +#define OBELISK_AST_FUNCTION_AST_H + +#include "ast/expression_ast.h" +#include "ast/prototype_ast.h" + +#include + +namespace obelisk +{ + class FunctionAST + { + private: + std::unique_ptr prototype_; + std::unique_ptr body_; + + std::unique_ptr getPrototype(); + void setPrototype(std::unique_ptr prototype); + + public: + FunctionAST(std::unique_ptr prototype, + std::unique_ptr body) : + prototype_(std::move(prototype)), + body_(std::move(body)) + { + } + }; +} // namespace obelisk + +#endif diff --git a/src/ast/meson.build b/src/ast/meson.build new file mode 100644 index 0000000..afb5a71 --- /dev/null +++ b/src/ast/meson.build @@ -0,0 +1,8 @@ +obelisk_ast_sources = files( + 'call_expression_ast.h', + 'expression_ast.h', + 'function_ast.h', + 'number_expression_ast.h', + 'prototype_ast.h', + 'variable_expression_ast.h' +) diff --git a/src/ast/number_expression_ast.h b/src/ast/number_expression_ast.h new file mode 100644 index 0000000..568e2d6 --- /dev/null +++ b/src/ast/number_expression_ast.h @@ -0,0 +1,24 @@ +#ifndef OBELISK_AST_NUMBER_EXPRESSION_AST_H +#define OBELISK_AST_NUMBER_EXPRESSION_AST_H + +#include "ast/expression_ast.h" + +namespace obelisk +{ + class NumberExpressionAST : public ExpressionAST + { + private: + double number_; + + double getNumber(); + void setNumber(double number); + + public: + NumberExpressionAST(double number) : + number_(number) + { + } + }; +} // namespace obelisk + +#endif diff --git a/src/ast/prototype_ast.h b/src/ast/prototype_ast.h new file mode 100644 index 0000000..c461fb8 --- /dev/null +++ b/src/ast/prototype_ast.h @@ -0,0 +1,34 @@ +#ifndef OBELISK_AST_PROTOTYPE_AST_H +#define OBELISK_AST_PROTOTYPE_AST_H + +#include +#include + +namespace obelisk +{ + class PrototypeAST + { + private: + std::string name_; + std::vector args_; + + void setName(const std::string& name); + std::vector getArgs(); + void setArgs(std::vector args); + + public: + PrototypeAST(const std::string& name, + std::vector args) : + name_(name), + args_(std::move(args)) + { + } + + const std::string& getName() const + { + return name_; + } + }; +} //namespace obelisk + +#endif diff --git a/src/ast/variable_expression_ast.h b/src/ast/variable_expression_ast.h new file mode 100644 index 0000000..6089080 --- /dev/null +++ b/src/ast/variable_expression_ast.h @@ -0,0 +1,25 @@ +#ifndef OBELISK_AST_VARIABLE_EXPRESSION_AST_H +#define OBELISK_AST_VARIABLE_EXPRESSION_AST_H + +#include "ast/expression_ast.h" + +#include + +namespace obelisk +{ + class VariableExpressionAST : public ExpressionAST + { + private: + std::string name_; + std::string getName(); + void setName(const std::string name); + + public: + VariableExpressionAST(const std::string &name) : + name_(name) + { + } + }; +} //namespace obelisk + +#endif diff --git a/src/lexer.cpp b/src/lexer.cpp new file mode 100644 index 0000000..6955b60 --- /dev/null +++ b/src/lexer.cpp @@ -0,0 +1,121 @@ +#include "lexer.h" + +#include + +int obelisk::Lexer::getToken() +{ + static int lastChar = ' '; + + while (isspace(lastChar)) + { + lastChar = getchar(); + } + + if (isalpha(lastChar)) + { + eraseIdentifier(); + appendIdentifier(lastChar); + while (isalnum((lastChar = getchar()))) + { + appendIdentifier(lastChar); + } + + if (getIdentifier() == "def") + { + return Token::kTokenDef; + } + + if (getIdentifier() == "extern") + { + return kTokenExtern; + } + + return kTokenIdentifier; + } + + if (isdigit(lastChar) || lastChar == '.') + { + std::string numberStr; + do + { + numberStr += lastChar; + lastChar = getchar(); + } + while (isdigit(lastChar) || lastChar == '.'); + + setNumberValue(strtod(numberStr.c_str(), nullptr)); + + return kTokenNumber; + } + + if (lastChar == '#') + { + commentLine(&lastChar); + + if (lastChar != EOF) + { + return getToken(); + } + } + else if (lastChar == '/') + { + lastChar = getchar(); + if (lastChar == '/') + { + commentLine(&lastChar); + + if (lastChar != EOF) + { + return getToken(); + } + } + } + + if (lastChar == EOF) + { + return kTokenEof; + } + + int thisChar = lastChar; + lastChar = getchar(); + return thisChar; +} + +void obelisk::Lexer::commentLine(int* lastChar) +{ + do + { + *lastChar = getchar(); + } + while (*lastChar != EOF && *lastChar != '\n' && *lastChar != '\r'); +} + +std::string obelisk::Lexer::getIdentifier() +{ + return identifier_; +} + +void obelisk::Lexer::setIdentifier(const std::string identifier) +{ + identifier_ = identifier; +} + +void obelisk::Lexer::eraseIdentifier() +{ + identifier_ = ""; +} + +void obelisk::Lexer::appendIdentifier(int lastChar) +{ + identifier_ += lastChar; +} + +double obelisk::Lexer::getNumberValue() +{ + return numberValue_; +} + +void obelisk::Lexer::setNumberValue(double numberValue) +{ + numberValue_ = numberValue; +} diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 0000000..20a0c96 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,46 @@ +#ifndef OBELISK_LEXER_H +#define OBELISK_LEXER_H + +#include + +namespace obelisk +{ + + class Lexer + { + private: + std::string identifier_; + double numberValue_; + + void setIdentifier(const std::string identifier); + void eraseIdentifier(); + void appendIdentifier(int lastChar); + void setNumberValue(double numberValue); + void commentLine(int* lastChar); + + public: + enum Token + { + kTokenInvalid = -1, + kTokenEof = -2, + + // commands + kTokenFact = -3, + kTokenRule = -4, + kTokenDef = -5, + kTokenExtern = -6, + + // primary + kTokenIdentifier = -7, + kTokenNumber = -8, + kTokenString = -9 + }; + + int getToken(); + + std::string getIdentifier(); + double getNumberValue(); + }; +} // namespace obelisk + +#endif diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..3be46cd --- /dev/null +++ b/src/meson.build @@ -0,0 +1,13 @@ +obelisk_sources = files( + 'obelisk.cpp', + 'lexer.cpp', + 'parser.cpp' +) + +subdir('ast') +obelisk_sources += obelisk_ast_sources + +executable('obelisk', + obelisk_sources, + install : true +) diff --git a/src/obelisk.cpp b/src/obelisk.cpp new file mode 100644 index 0000000..90bf451 --- /dev/null +++ b/src/obelisk.cpp @@ -0,0 +1,50 @@ +#include "lexer.h" +#include "obelisk.h" +#include "parser.h" + +#include + +static void mainLoop() +{ + obelisk::Parser* parser = new obelisk::Parser(); + + // Prime the first token. + fprintf(stderr, "ready> "); + parser->getNextToken(); + + while (true) + { + fprintf(stderr, "ready> "); + switch (parser->getCurrentToken()) + { + case obelisk::Lexer::kTokenEof : + return; + case obelisk::Lexer::kTokenInvalid : + std::cerr << "Invalid token!\n"; + parser->getNextToken(); + return; + case ';' : // ignore top-level semicolons. + std::cout << "Identifier: " + << parser->getLexer()->getIdentifier() << std::endl; + std::cout << "Num: " << parser->getLexer()->getNumberValue() + << std::endl; + parser->getNextToken(); + break; + default : + parser->getNextToken(); + break; + } + } +} + +int main(int argc, char** argv) +{ + for (int i = 1; i < argc; i++) + { + std::cout << argv[i] << std::endl; + } + + mainLoop(); + + return EXIT_SUCCESS; +} diff --git a/src/obelisk.h b/src/obelisk.h new file mode 100644 index 0000000..d964df7 --- /dev/null +++ b/src/obelisk.h @@ -0,0 +1 @@ +static void mainLoop(); diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..9c783b5 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,225 @@ +#include "ast/call_expression_ast.h" +#include "ast/number_expression_ast.h" +#include "ast/variable_expression_ast.h" +#include "parser.h" + +obelisk::Parser::Parser() +{ + lexer_ = new obelisk::Lexer(); +} + +obelisk::Parser::Parser(obelisk::Lexer* lexer) +{ + if (lexer != nullptr) + { + lexer_ = lexer; + } + else + { + Parser(); + } +} + +obelisk::Parser::~Parser() +{ + delete lexer_; +} + +obelisk::Lexer* obelisk::Parser::getLexer() +{ + return lexer_; +} + +int obelisk::Parser::getNextToken() +{ + setCurrentToken(getLexer()->getToken()); + return getCurrentToken(); +} + +int obelisk::Parser::getCurrentToken() +{ + return currentToken_; +} + +void obelisk::Parser::setCurrentToken(int currentToken) +{ + currentToken_ = currentToken; +} + +std::unique_ptr obelisk::Parser::logError( + const char* str) +{ + fprintf(stderr, "Error: %s\n", str); + return nullptr; +} + +std::unique_ptr obelisk::Parser::logErrorPrototype( + const char* str) +{ + logError(str); + return nullptr; +} + +std::unique_ptr obelisk::Parser::parseExpression() +{ + auto LHS = parsePrimary(); + if (!LHS) + { + return nullptr; + } + + return LHS; +} + +std::unique_ptr obelisk::Parser::parsePrimary() +{ + switch (getCurrentToken()) + { + case obelisk::Lexer::kTokenIdentifier : + return parseIdentifierExpression(); + case obelisk::Lexer::kTokenNumber : + return parseNumberExpression(); + case '(' : + return parseParenthesisExpression(); + default : + return logError("unknown token when expecting and expression"); + } +} + +std::unique_ptr obelisk::Parser::parseNumberExpression() +{ + auto result = std::make_unique( + getLexer()->getNumberValue()); + getNextToken(); + return std::move(result); +} + +std::unique_ptr + obelisk::Parser::parseParenthesisExpression() +{ + getNextToken(); + auto v = parseExpression(); + if (!v) + { + return nullptr; + } + + if (getCurrentToken() != ')') + { + return logError("expected ')'"); + } + getNextToken(); + return v; +} + +std::unique_ptr + obelisk::Parser::parseIdentifierExpression() +{ + std::string idName = getLexer()->getIdentifier(); + getNextToken(); + if (getCurrentToken() != '(') + { + return std::make_unique(idName); + } + + getNextToken(); + std::vector> args; + if (getCurrentToken() != ')') + { + while (true) + { + if (auto arg = parseExpression()) + { + args.push_back(std::move(arg)); + } + else + { + return nullptr; + } + + if (getCurrentToken() == ')') + { + break; + } + + if (getCurrentToken() != ',') + { + return logError("Expected ')' or ',' in argument list"); + } + + getNextToken(); + } + } + + getNextToken(); + return std::make_unique(idName, std::move(args)); +} + +std::unique_ptr obelisk::Parser::parsePrototype() +{ + if (getCurrentToken() != obelisk::Lexer::kTokenIdentifier) + { + return logErrorPrototype("Expected function name in prototype"); + } + + std::string functionName = getLexer()->getIdentifier(); + getNextToken(); + + if (getCurrentToken() != '(') + { + return logErrorPrototype("Expected '(' in prototype"); + } + + std::vector argNames; + while (getNextToken() == obelisk::Lexer::kTokenIdentifier) + { + argNames.push_back(getLexer()->getIdentifier()); + } + + if (getCurrentToken() != ')') + { + return logErrorPrototype("Expected ')' in prototype"); + } + + getNextToken(); + + return std::make_unique(functionName, + std::move(argNames)); +} + +std::unique_ptr obelisk::Parser::parseDefinition() +{ + getNextToken(); + auto prototype = parsePrototype(); + if (!prototype) + { + return nullptr; + } + + if (auto expression = parseExpression()) + { + return std::make_unique(std::move(prototype), + std::move(expression)); + } + + return nullptr; +} + +std::unique_ptr obelisk::Parser::parseTopLevelExpression() +{ + if (auto expression = parseExpression()) + { + // Make an anonymous prototype + auto prototype = std::make_unique("__anon_expr", + std::vector()); + return std::make_unique(std::move(prototype), + std::move(expression)); + } + return nullptr; +} + +std::unique_ptr obelisk::Parser::parseExtern() +{ + getNextToken(); + return parsePrototype(); +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..2a00bbc --- /dev/null +++ b/src/parser.h @@ -0,0 +1,53 @@ +#ifndef OBELISK_PARSER_H +#define OBELISK_PARSER_H + +#include "ast/expression_ast.h" +#include "ast/function_ast.h" +#include "ast/prototype_ast.h" +#include "lexer.h" + +#include + +namespace obelisk +{ + class Parser + { + private: + obelisk::Lexer* lexer_; + int currentToken_; + + void setCurrentToken(int currentToken); + + std::unique_ptr logError(const char* str); + std::unique_ptr logErrorPrototype( + const char* str); + + std::unique_ptr parseExpression(); + std::unique_ptr parseNumberExpression(); + std::unique_ptr + parseParenthesisExpression(); + std::unique_ptr parseIdentifierExpression(); + std::unique_ptr parsePrimary(); + std::unique_ptr parsePrototype(); + std::unique_ptr parseDefinition(); + std::unique_ptr parseTopLevelExpression(); + std::unique_ptr parseExtern(); + + public: + Parser(); + Parser(obelisk::Lexer* lexer); + ~Parser(); + + obelisk::Lexer* getLexer(); + + int getCurrentToken(); + + int getNextToken(); + + void handleDefinition(); + void handleExtern(); + void handleTopLevelExpression(); + }; +} // namespace obelisk + +#endif From 20192152d16f603f51ed0a2b93d488069dd6e14f Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Tue, 25 Oct 2022 17:08:36 -0300 Subject: [PATCH 3/3] start working on the base of knowledge base --- src/knowledge_base.cpp | 59 ++++++++++++++++++++++++++++++++++++++++ src/knowledge_base.h | 61 ++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 6 ++++- src/models/entity.cpp | 30 +++++++++++++++++++++ src/models/entity.h | 26 ++++++++++++++++++ src/models/verb.cpp | 36 +++++++++++++++++++++++++ src/models/verb.h | 27 +++++++++++++++++++ src/obelisk.cpp | 40 +++++++++++++++++++++++++++ 8 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/knowledge_base.cpp create mode 100644 src/knowledge_base.h create mode 100644 src/models/entity.cpp create mode 100644 src/models/entity.h create mode 100644 src/models/verb.cpp create mode 100644 src/models/verb.h diff --git a/src/knowledge_base.cpp b/src/knowledge_base.cpp new file mode 100644 index 0000000..b70d004 --- /dev/null +++ b/src/knowledge_base.cpp @@ -0,0 +1,59 @@ +#include "knowledge_base.h" + +#include + +const std::string obelisk::Sql::create_facts_table + = "CREATE TABLE fact (id INT PRIMARY KEY NOT NULL);"; + +obelisk::KnowledgeBase::KnowledgeBase(const char* filename) +{ + KnowledgeBase(filename, DEFAULT_FLAGS); +} + +obelisk::KnowledgeBase::KnowledgeBase(const char* filename, int flags) +{ + filename_ = std::move(filename); + flags_ = std::move(flags); + + auto result = sqlite3_open_v2(filename, &dbConnection_, flags, NULL); + if (result != SQLITE_OK) + { + logSqliteError(result); + } + + char* tmp; + result = sqlite3_exec(dbConnection_, + obelisk::Sql::create_facts_table.c_str(), + NULL, + NULL, + &tmp); + if (tmp) + { + std::string errmsg(tmp); + throw obelisk::KnowledgeBaseException(errmsg); + } +} + +obelisk::KnowledgeBase::~KnowledgeBase() +{ + sqlite3_close_v2(dbConnection_); +} + +// TODO: log files? +void obelisk::KnowledgeBase::logSqliteError(int result) +{ + std::cout << sqlite3_errstr(result) << std::endl; +} + +void obelisk::KnowledgeBase::getFloat(float* result1, + float* result2, + double var) +{ + *result1 = (float) var; + *result2 = (float) (var - (double) *result1); +} + +void obelisk::KnowledgeBase::getDouble(double* result, float var1, float var2) +{ + *result = (double) ((double) var2 + (double) var1); +} diff --git a/src/knowledge_base.h b/src/knowledge_base.h new file mode 100644 index 0000000..aa8f09b --- /dev/null +++ b/src/knowledge_base.h @@ -0,0 +1,61 @@ +#ifndef OBELISK_KNOWLEDGE_BASE_H +#define OBELISK_KNOWLEDGE_BASE_H + +#include + +#include +#include + +namespace obelisk +{ + class KnowledgeBase + { + private: + const int DEFAULT_FLAGS + = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + const char* filename_; + sqlite3* dbConnection_; + int flags_; + void logSqliteError(int result); + + public: + KnowledgeBase(const char* filename); + KnowledgeBase(const char* filename, int flags); + ~KnowledgeBase(); + + template + int addFacts(std::string verb, T leftEntities, U rightEntities); + // TODO: add parameter for fact + template + int addRules(std::string verb, T leftEntities, U rightEntities); + template int addActions(); + + void getDouble(double* result, float var1, float var2); + void getFloat(float* result1, float* result2, double var); + }; + + class Sql + { + public: + static const std::string create_facts_table; + }; + + class KnowledgeBaseException : public std::exception + { + private: + std::string errorMessage_; + + public: + KnowledgeBaseException(std::string errorMessage) : + errorMessage_(errorMessage) + { + } + + const char* what() const noexcept + { + return errorMessage_.c_str(); + } + }; +} // namespace obelisk + +#endif diff --git a/src/meson.build b/src/meson.build index 3be46cd..d7c5d50 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,13 +1,17 @@ obelisk_sources = files( 'obelisk.cpp', 'lexer.cpp', - 'parser.cpp' + 'parser.cpp', + 'knowledge_base.cpp' ) +sqlite3 = dependency('sqlite3') + subdir('ast') obelisk_sources += obelisk_ast_sources executable('obelisk', obelisk_sources, + dependencies : [sqlite3], install : true ) diff --git a/src/models/entity.cpp b/src/models/entity.cpp new file mode 100644 index 0000000..c2d29bd --- /dev/null +++ b/src/models/entity.cpp @@ -0,0 +1,30 @@ +#include "models/entity.h" + +obelisk::Entity::Entity() +{ +} + +obelisk::Entity::Entity(std::string name) +{ + name = name; +} + +int obelisk::Entity::getId() +{ + return id_; +} + +void obelisk::Entity::setId(int id) +{ + id_ = id; +} + +std::string obelisk::Entity::getName() +{ + return name_; +} + +void obelisk::Entity::setName(std::string name) +{ + name_ = name_; +} diff --git a/src/models/entity.h b/src/models/entity.h new file mode 100644 index 0000000..4663fbf --- /dev/null +++ b/src/models/entity.h @@ -0,0 +1,26 @@ +#ifndef OBELISK_MODELS_ENTITY_H +#define OBELISK_MODELS_ENTITY_H + +#include + +namespace obelisk +{ + class Entity + { + private: + int id_; + std::string name_; + + public: + Entity(); + Entity(std::string name); + + int getId(); + void setId(int id); + + std::string getName(); + void setName(std::string name); + }; +} // namespace obelisk + +#endif diff --git a/src/models/verb.cpp b/src/models/verb.cpp new file mode 100644 index 0000000..eab0251 --- /dev/null +++ b/src/models/verb.cpp @@ -0,0 +1,36 @@ +#include "models/verb.h" + +obelisk::Verb::Verb() +{ +} + +obelisk::Verb::Verb(std::string verb) +{ + verb_ = verb; +} + +obelisk::Verb::Verb(int id, std::string verb) +{ + id_ = id; + verb_ = verb; +} + +int obelisk::Verb::getId() +{ + return id_; +} + +void obelisk::Verb::setId(int id) +{ + id_ = id; +} + +std::string obelisk::Verb::getVerb() +{ + return verb_; +} + +void obelisk::Verb::setVerb(std::string verb) +{ + verb_ = verb; +} diff --git a/src/models/verb.h b/src/models/verb.h new file mode 100644 index 0000000..1919bef --- /dev/null +++ b/src/models/verb.h @@ -0,0 +1,27 @@ +#ifndef OBELISK_MODELS_VERB_H +#define OBELISK_MODELS_VERB_H + +#include + +namespace obelisk +{ + class Verb + { + private: + int id_; + std::string verb_; + + public: + Verb(); + Verb(std::string verb); + Verb(int id, std::string verb); + + int getId(); + void setId(int id); + + std::string getVerb(); + void setVerb(std::string verb); + }; +} // namespace obelisk + +#endif diff --git a/src/obelisk.cpp b/src/obelisk.cpp index 90bf451..5e46be6 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -1,8 +1,11 @@ +#include "knowledge_base.h" #include "lexer.h" #include "obelisk.h" #include "parser.h" +#include #include +#include static void mainLoop() { @@ -44,6 +47,43 @@ int main(int argc, char** argv) std::cout << argv[i] << std::endl; } + try + { + obelisk::KnowledgeBase* kb = new obelisk::KnowledgeBase("cromer.kb"); + + /*std::vector leftObjects; + std::vector rightObjects; + leftObjects.push_back("chris"); + leftObjects.push_back("martin"); + + rightObjects.push_back("happy"); + rightObjects.push_back("smart"); + + kb->addFacts("is", leftObjects, rightObjects);*/ + } + catch (obelisk::KnowledgeBaseException& exception) + { + std::cout << exception.what() << std::endl; + return EXIT_FAILURE; + } + + // This can be used to store a double as 2 floats in the database, then restore it back to a double. + // Inspired by Godot's double precision on the GPU to render large worlds. + /*float first; + float second; + double var = 0.123456789012345; + kb->getFloat(&first, &second, var); + std::cout << std::setprecision(std::numeric_limits::digits10) + << "Double: " << var << std::endl + << "First: " << first << std::endl + << "Second: " << second << std::endl; + var = 0.0; + kb->getDouble(&var, first, second); + std::cout << std::setprecision(std::numeric_limits::digits10) + << "Double: " << var << std::endl + << "First: " << first << std::endl + << "Second: " << second << std::endl;*/ + mainLoop(); return EXIT_SUCCESS;