commit 02afb71c84209edc6e56c4424a2aae4e30b9ad2b Author: hans Date: Sat Nov 6 00:24:04 2021 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2f2a16a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) + +project(biscuit_interpreter) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_executable(biscuit_interpreter) + +target_sources( biscuit_interpreter PRIVATE + src/main.cpp + src/parse.cpp + src/keywords.cpp ) + +include_directories(${CMAKE_SOURCE_DIR}) diff --git a/biscuit_doc.odt b/biscuit_doc.odt new file mode 100644 index 0000000..3036d75 Binary files /dev/null and b/biscuit_doc.odt differ diff --git a/examples/helloworld.bisc b/examples/helloworld.bisc new file mode 100644 index 0000000..79b1c55 --- /dev/null +++ b/examples/helloworld.bisc @@ -0,0 +1,2 @@ +print #hello_world +print 1.01234 diff --git a/src/.ycm_extra_conf.py b/src/.ycm_extra_conf.py new file mode 100644 index 0000000..89f70f2 --- /dev/null +++ b/src/.ycm_extra_conf.py @@ -0,0 +1,18 @@ +import os +import ycm_core + +flags = [ + '-Wall', + '-Wextra', + '-Wno-long-long', + '-Wno-variadic-macros', + '-std=c++20', + ] + +SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', ] + +def FlagsForFile( filename, **kwargs ): + return { + 'flags': flags, + 'do_cache': True + } diff --git a/src/keywords.cpp b/src/keywords.cpp new file mode 100644 index 0000000..38ddd42 --- /dev/null +++ b/src/keywords.cpp @@ -0,0 +1,18 @@ +#include +#include + +#include "keywords.hpp" + +const std::vector keywords = +{ + { + .name = "print", + .expected_num_args = -1, + .expected_token_types = {TokenType::ID_OR_LIT}, + .expected_value_types = {ValueType::ANY} + }, + { + .name = "exit", + .expected_num_args = 0 + } +}; diff --git a/src/keywords.hpp b/src/keywords.hpp new file mode 100644 index 0000000..385cab0 --- /dev/null +++ b/src/keywords.hpp @@ -0,0 +1,20 @@ +#ifndef BISCUIT_KEYWORDS_HPP_INCLUDED +#define BISCUIT_KEYWORDS_HPP_INCLUDED + +#include +#include +#include +#include "parse.hpp" + +struct Keyword +{ + const std::string name; + const int expected_num_args; // -1 means: 1 to infinity, in which case all args are of same type + + const std::vector expected_token_types; + const std::vector expected_value_types; +}; + +extern const std::vector keywords; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..247b465 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "parse.hpp" + +std::ifstream get_infile(int argc, char ** argv) +{ + if(argc != 2) + { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + exit(EXIT_FAILURE); + } + + std::ifstream infile(argv[1]); + + if(!infile) + { + std::cerr << "Could not open '" << argv[1] << "'" << std::endl; + exit(EXIT_FAILURE); + } + + return infile; +} + +int main(int argc, char * argv[]) +{ + std::ifstream infile = get_infile(argc, argv); + std::string word; + while(infile >> word) + { + try + { + Symbol sym(word); + sym.print(); + } + catch(MalformedIdentifierExcept& exc) + { + std::cout << "Malformed identifier: '" << exc.malformed_str << "'" + << "\nAborting..." << std::endl; + exit(EXIT_FAILURE); + } + catch(UnknownKeywordExcept& exc) + { + std::cout << "Unknown Keyword: '" << exc.unknown_keyword << "'" + << "\nAborting..." << std::endl; + exit(EXIT_FAILURE); + } + catch(WrongSymbolExcept& exc) + { + std::cout << "Wrong symbol type\n Aborting..." << std::endl; + exit(EXIT_FAILURE); + } + } + + return 0; +} diff --git a/src/parse.cpp b/src/parse.cpp new file mode 100644 index 0000000..2d2c777 --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "keywords.hpp" +#include "parse.hpp" + +bool is_keyword(std::string str) +{ + for(char &c : str) + c = std::tolower(c); + + for(auto &keyword : keywords) + if(str == keyword.name) + return true; + return 0; +} + +bool is_string_literal(const std::string& str) +{ + if(str[0] == '#') + return true; + else + return false; +} + +bool is_decimal_literal(const std::string& str) +{ + for(const char &c : str) + if(!std::isdigit(c) && c != '.') + return false; + + return true; +} + +bool is_identifier(const std::string& str) +{ + for(const char& c : str) + if(!(std::isalpha(c) || c == '_')) + return false; + return true; +} + + +bool Token::parse_as_keyword(const std::string &str) +{ + if(!is_keyword(str)) + return false; + + type = TokenType::KEYWORD; + val_string = str; + + return true; +} + + +bool Token::parse_as_literal(const std::string &str) +{ + if(is_string_literal(str)) + { + type = TokenType::LITERAL; + literal_type = ValueType::STRING; + auto str_copy = str; + str_copy.erase(0, 1); + val_string = str_copy; + return true; + } + else if(is_decimal_literal(str)) + { + type = TokenType::LITERAL; + literal_type = ValueType::DECIMAL; + val_float = std::stof(str); + val_string = str; + return true; + } + else + { + return false; + } +} + +bool Token::parse_as_identifier(const std::string &str) +{ + if(is_identifier(str)) + { + type = TokenType::IDENTIFIER; + val_string = str; + } + return false; +} + +Token::Token(const std::string &str) +{ + if (parse_as_keyword(str)); + else if(parse_as_literal(str)); + else if(parse_as_identifier(str)); + else + { + throw MalformedIdentifierExcept(str); + } +} + +void Token::print() const +{ + switch(type) + { + case TokenType::IDENTIFIER: + std::cout << "Identifier: \"" << val_string << "\""; + break; + case TokenType::KEYWORD: + std::cout << "Keyword: \"" << val_string << "\""; + break; + + case TokenType::LITERAL: + std::cout << "Literal of type "; + if(literal_type == ValueType::DECIMAL) + std::cout << "decimal: " << val_float; + else if(literal_type == ValueType::STRING) + std::cout << "string: \"" << val_string << "\""; + break; + case TokenType::ID_OR_LIT: + std::cerr << "Identifier or Literal?! Something went terribly wrong!!"; + } + + std::cout << std::endl; +} + + + +Instruction::Instruction(std::vector& _token_list) +{ + tokens = std::move(_token_list); + + // resolve our keyword + auto keyword_it = std::find(keywords.begin(), keywords.end(), tokens[0].val_string); + + // Throw if unknown + if(keyword_it == keywords.end()) + throw UnknownKeywordExcept(tokens[0].val_string); + else + keyword_ptr = &*keyword_it; + + // Make sure the keyword gets the number of arguments it needs + if(keyword_ptr->expected_num_args != -1 && (keyword_ptr->expected_num_args != tokens.size() - 1)) + throw WrongArgumentCountExcept(keyword_ptr->name, keyword_ptr->expected_num_args, tokens.size() - 1); + + +} diff --git a/src/parse.hpp b/src/parse.hpp new file mode 100644 index 0000000..aea1bf8 --- /dev/null +++ b/src/parse.hpp @@ -0,0 +1,94 @@ +#ifndef BISCUIT_PARSE_HPP_INCLUDED +#define BISCUIT_PARSE_HPP_INCLUDED + +#include +#include +#include +#include "keywords.hpp" + +enum TokenType +{ + KEYWORD, + IDENTIFIER, + LITERAL, + ID_OR_LIT +}; + +enum ValueType +{ + STRING, + DECIMAL, + ANY +}; + +struct MalformedIdentifierExcept : public std::exception +{ + std::string malformed_str; + MalformedIdentifierExcept(const std::string& arg_str) + : malformed_str(arg_str) {} +}; + +struct UnknownKeywordExcept : public std::exception +{ + std::string unknown_keyword; + UnknownKeywordExcept(const std::string& arg_str) + : unknown_keyword(arg_str) {} +}; + +struct WrongArgumentCountExcept : public std::exception +{ + int expected, got; + std::string keyword_name; + WrongArgumentCountExcept(std::string _name, int _expected, int _got) + : expected(_expected), got(_got), keyword_name(_name) {} +}; + +struct WrongTokenExcept : public std::exception +{ + TokenType expected, got; + std::string keyword_name; + WrongTokenExcept(std::string _name, const TokenType& _expected, const TokenType& _got) + : expected(_expected), got(_got), keyword_name(_name) {} +}; + +struct TypeErrorExcept : public std::exception +{ + ValueType expected, got; + std::string keyword_name; + TypeErrorExcept(std::string _name, const ValueType& _expected, const ValueType& _got) + : expected(_expected), got(_got), keyword_name(_name) {} +}; + + +struct Token +{ +private: + bool parse_as_keyword(const std::string &str); + + bool parse_as_literal(const std::string &str); + + // if a word is not a keyword or literal, it must be identifier + bool parse_as_identifier(const std::string &str); + +public: + enum TokenType type; + enum ValueType literal_type; + float val_float; + std::string val_string; + + Token(const std::string &str); + + void print() const; +}; + +class Instruction +{ + std::vector tokens; + Keyword *keyword_ptr; + +public: + // Try to parse a list of symbols as instructions + Instruction(std::vector& _token_list); +}; + +#endif diff --git a/src/runtime.hpp b/src/runtime.hpp new file mode 100644 index 0000000..434deb3 --- /dev/null +++ b/src/runtime.hpp @@ -0,0 +1,11 @@ +#ifndef BISCUIT_RUNTIME_HPP_INCLUDED +#define BISCUIT_RUNTIME_HPP_INCLUDED + +#include + +struct Context +{ + std::vector< +}; + +#endif