#include #include #include #include #include #include #include #include #include #include #include #include "../libs/linenoise.hpp" enum class ValueTypes { Identifier, Int, Double, String, List, Function, Type, Nil, Auto }; enum class ParserErrorType { UnexpectedEndOfInput, UnexpectedToken, MissingOpenParen, MissingCloseParen, UnexpectedTopLevelToken }; enum class InterpreterErrorType { UnknownInstruction, UnexpectedToken, IncorrectTokenType, MathError, UnknownEnvironment }; enum class CTranspilerErrorType { UnknownInstruction, UnexpectedToken, IncorrectTokenType, UnknownEnvironment }; class CTranspilerError : public std::exception { std::string message; CTranspilerErrorType type; public: explicit CTranspilerError(CTranspilerErrorType type) : type(type) { std::stringstream ss; ss << "Compiling error: "; switch (type) { case CTranspilerErrorType::UnknownInstruction: ss << "UnknownInstruction"; break; case CTranspilerErrorType::UnexpectedToken: ss << "UnexpectedToken"; break; case CTranspilerErrorType::IncorrectTokenType: ss << "IncorrectTokenType"; break; case CTranspilerErrorType::UnknownEnvironment: ss << "UnknownEnvironment"; break; } message = ss.str(); } [[nodiscard]] const char *what() const noexcept override { return message.c_str(); } }; class InterpretingError : public std::exception { std::string message; InterpreterErrorType errorType; public: explicit InterpretingError(InterpreterErrorType errorType) : errorType(errorType) { std::stringstream oss; oss << "Interpreting error: "; switch (errorType) { case InterpreterErrorType::UnexpectedToken: oss << "Unexpected token"; break; case InterpreterErrorType::UnknownInstruction: oss << "Unknown instruction"; break; case InterpreterErrorType::IncorrectTokenType: oss << "Incorrect token type"; break; case InterpreterErrorType::MathError: oss << "Math error"; break; case InterpreterErrorType::UnknownEnvironment: oss << "Unknown environment"; break; } message = oss.str(); } [[nodiscard]] const char* what() const noexcept override { return message.c_str(); } }; class ParsingError : public std::exception { std::string message; ParserErrorType errorType; int position; std::string sourceCode; public: ParsingError(ParserErrorType type, int pos, std::string source = "") : errorType(type), position(pos), sourceCode(std::move(source)) { std::ostringstream oss; oss << "Parsing error at position " << pos << ": "; switch (type) { case ParserErrorType::UnexpectedEndOfInput: oss << "Unexpected end of input"; break; case ParserErrorType::UnexpectedToken: oss << "Unexpected token"; break; case ParserErrorType::MissingOpenParen: oss << "Expected '(' at start of expression"; break; case ParserErrorType::MissingCloseParen: oss << "Missing closing ')'"; break; case ParserErrorType::UnexpectedTopLevelToken: oss << "Unexpected token at top level (expected '(')"; break; } message = oss.str(); } [[nodiscard]] const char* what() const noexcept override { return message.c_str(); } [[nodiscard]] ParserErrorType getErrorType() const { return errorType; } [[nodiscard]] int getPosition() const { return position; } [[nodiscard]] const std::string& getSourceCode() const { return sourceCode; } }; class Instruction; class Function { public: ValueTypes returnType = ValueTypes::Nil; std::vector> arguments; std::vector code; Function(ValueTypes returnType, std::vector> args, std::vector code) : returnType(returnType), arguments(std::move(args)), code(std::move(code)) {} Function() = default; }; class Value { std::variant, Function, ValueTypes> value; public: ValueTypes type; [[nodiscard]] std::optional getInt() const { if (std::holds_alternative(value)) { return std::get(value); } return {}; } [[nodiscard]] std::optional getDouble() const { if (std::holds_alternative(value)) { return std::get(value); } return {}; } [[nodiscard]] std::optional getString() const { if (std::holds_alternative(value)) { return std::get(value); } return {}; } [[nodiscard]] std::optional> getList() const { if (std::holds_alternative>(value)) { return std::get>(value); } return {}; } [[nodiscard]] std::optional getFunction() const { if (std::holds_alternative(value)) { return std::get(value); } return {}; } [[nodiscard]] std::optional getType() const { if (std::holds_alternative(value)) { return std::get(value); } return {}; } void print() const { switch (type) { case ValueTypes::String: { std::cout << getString().value(); break; } case ValueTypes::Int: { std::cout << getInt().value(); break; } case ValueTypes::Double: { std::cout << getDouble().value(); break; } case ValueTypes::List: { auto list = getList().value(); bool first = true; for (auto& listElement : list) { if (!first) { std::cout << ", "; } else { first = false; } listElement.print(); } break; } case ValueTypes::Function: { std::cout << ""; break; } case ValueTypes::Type: { std::cout << ""; break; } default: case ValueTypes::Nil: { std::cout << "\033[2;3;96m" << "nil" << "\033[0m"; } } } bool operator==(Value &otherValue) const { if (type != otherValue.type) { return false; } switch (type) { case ValueTypes::String: { if (getString() == otherValue.getString()) { return true; } return false; } case ValueTypes::Int: { if (getInt() == otherValue.getInt()) { return true; } return false; } case ValueTypes::Double: { if (getDouble() == otherValue.getDouble()) { return true; } return false; } case ValueTypes::List: { auto list = getList().value(); auto compareList = otherValue.getList().value(); if (list.size() != compareList.size()) { return false; } for (int i = 0; i < list.size(); i++) { if (list[i] == compareList[i]) { return true; } return false; } return true; } default: return false; } } friend std::ostream& operator<<(std::ostream &os, const Value &obj) { switch (obj.type) { case ValueTypes::String: { os << obj.getString().value(); break; } case ValueTypes::Int: { os << obj.getInt().value(); break; } case ValueTypes::Double: { os << obj.getDouble().value(); break; } case ValueTypes::List: { auto list = obj.getList().value(); bool first = true; for (auto& listElement : list) { if (!first) { os << ", "; } else { first = false; } listElement.print(); } break; } case ValueTypes::Function: { os << ""; break; } case ValueTypes::Type: { os << ""; break; } default: case ValueTypes::Nil: { os << "\033[2;3;96m" << "nil" << "\033[0m"; } } return os; } explicit Value() : value(nullptr), type(ValueTypes::Nil) {} explicit Value(int in) : value(in), type(ValueTypes::Int) {} explicit Value(double in) : value(in), type(ValueTypes::Double) {} explicit Value(std::string in) : value(in), type(ValueTypes::String) {} explicit Value(std::vector in) : value(in), type(ValueTypes::List) {} explicit Value(Function in) : value(in), type(ValueTypes::Function) {} explicit Value(ValueTypes in) : value(in), type(ValueTypes::Type) {} Value(std::string in, bool isIdentifier) : value(in), type(isIdentifier ? ValueTypes::Identifier : ValueTypes::String) {} }; class Instruction { public: std::string instruction; std::vector> args; Instruction(std::string instruction, std::vector> args) : instruction(std::move(instruction)), args(std::move(args)) {} Instruction() = default; }; class Parser { std::string input; std::vector split; int currentChar = -1; int current = -1; std::optional consumeChar() { currentChar++; if (currentChar >= input.size()) { return {}; } return input[currentChar]; } std::optional consume() { current++; if (current >= split.size()) { return {}; } return split[current]; } [[nodiscard]] std::optional peek() const { if (current + 1 >= split.size()) { return {}; } return split[current + 1]; } // Helper to detect value type static Value parseValue(const std::string& token) { // If it's nil, just return nil if (token == "nil") { return Value(); } // Also check the types if (token == "int") { return Value(ValueTypes::Int); } if (token == "double") { return Value(ValueTypes::Double); } if (token == "string") { return Value(ValueTypes::String); } if (token == "list") { return Value(ValueTypes::List); } if (token == "function") { return Value(ValueTypes::Function); } if (token == "type") { return Value(ValueTypes::Type); } // Check if it's a string literal if (token.front() == '"' && token.back() == '"') { return Value(token.substr(1, token.length() - 2)); } // Try to parse as integer bool isInt = true; bool isDouble = false; for (size_t i = 0; i < token.length(); i++) { char c = token[i]; if (i == 0 && c == '-') continue; // Allow negative sign if (c == '.') { if (isDouble) { // Second dot isInt = false; break; } isDouble = true; isInt = false; } else if (!std::isdigit(c)) { isInt = false; isDouble = false; break; } } if (isInt) { return Value(std::stoi(token)); } if (isDouble) { return Value(std::stod(token)); } // If it isn't any value, return an identifier return {token, true}; } // Recursive parser for expressions Instruction parseExpression() { auto topt = consume(); if (!topt) { throw ParsingError(ParserErrorType::UnexpectedEndOfInput, current, input); } std::string token = topt.value(); if (token != "(") { throw ParsingError(ParserErrorType::MissingOpenParen, current, input); } topt = consume(); if (!topt) { throw ParsingError(ParserErrorType::UnexpectedEndOfInput, current, input); } std::string instruction = topt.value(); std::vector> args; while (true) { topt = peek(); if (!topt) { throw ParsingError(ParserErrorType::MissingCloseParen, current, input); } token = topt.value(); if (token == ")") { consume(); break; } else if (token == "(") { args.emplace_back(parseExpression()); } else { consume(); args.emplace_back(parseValue(token)); } } return {instruction, args}; } public: std::vector instructions; explicit Parser(std::string in) : input(std::move(in)) { // Lexer logic std::string buf; bool inString = false; while (auto copt = consumeChar()) { char c = copt.value(); if (c != '"' && inString) { buf.push_back(c); continue; } switch (c) { case '(': case ')': case '[': case ']': { if (!buf.empty()) { split.push_back(buf); buf.clear(); } split.emplace_back(1, c); break; } case ' ': case '\n': if (!buf.empty()) { split.push_back(buf); buf.clear(); } break; case '"': { inString = !inString; buf.push_back('"'); if (!inString) { split.push_back(buf); buf.clear(); } break; } default: { buf.push_back(c); } } } // Parser logic - parse all top-level expressions while (peek()) { auto token = peek().value(); if (token == "(") { instructions.push_back(parseExpression()); } else { throw ParsingError(ParserErrorType::UnexpectedTopLevelToken, current + 1, input); } } } Parser() = default; }; class Interpreter { public: std::vector instructions = {}; Value returnValue; std::map environment = {{"pippleVersion", Value("0.0.1")}}; Value interpretInstruction(Instruction& instruction) { // Preprocess identifiers for (auto& arg : instruction.args) { if (std::holds_alternative(arg) && instruction.instruction != "let" && instruction.instruction != "set" && instruction.instruction != "function") { if (std::get(arg).type == ValueTypes::Identifier) { std::string id = std::get(arg).getString().value(); if (!environment.contains(id)) { throw InterpretingError(InterpreterErrorType::UnknownEnvironment); } arg = environment[id]; } } } // Instructions at the top of this function require input to not be preprocessed if (instruction.instruction == "if") { if (instruction.args.size() < 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } bool toContinue = true; if (std::holds_alternative(instruction.args[0])) { toContinue = interpretInstruction(std::get(instruction.args[0])).type != ValueTypes::Nil; } if (std::holds_alternative(instruction.args[0])) { toContinue = std::get(instruction.args[0]).type != ValueTypes::Nil; } if (toContinue) { bool first = true; Value returnValue; for (auto& arg : instruction.args) { if (first) { first = false; } else { if (std::holds_alternative(arg)) { returnValue = interpretInstruction(std::get(arg)); } } } return returnValue; } return Value(); } if (instruction.instruction == "while") { if (instruction.args.size() < 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } while (true) { Value conditionResult; if (std::holds_alternative(instruction.args[0])) { Instruction condCopy = std::get(instruction.args[0]); conditionResult = interpretInstruction(condCopy); } else if (std::holds_alternative(instruction.args[0])) { conditionResult = std::get(instruction.args[0]); } if (conditionResult.type == ValueTypes::Nil) { break; } for (size_t i = 1; i < instruction.args.size(); i++) { if (std::holds_alternative(instruction.args[i])) { Instruction bodyCopy = std::get(instruction.args[i]); if (bodyCopy.instruction == "break") { return Value(); } interpretInstruction(bodyCopy); } } } return Value(); } if (instruction.instruction == "function") { // (function returntype [valtype argname valtype argname] (code) (morecode)) // returnType if (std::holds_alternative(instruction.args[0])) { instruction.args[0] = interpretInstruction(std::get(instruction.args[0])); } auto returnTypeValue = std::get(instruction.args[0]); if (returnTypeValue.type != ValueTypes::Type) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } ValueTypes returnType = returnTypeValue.getType().value(); std::vector> args; // [valtype argname valtype argname] int argAmount = 0; if (std::holds_alternative(instruction.args[1]) && std::get(instruction.args[1]).type == ValueTypes::Identifier && std::get(instruction.args[1]).getString().value() != "[") { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } for (int i = 2; i < instruction.args.size(); i += 2) { if (std::holds_alternative(instruction.args[i])) { instruction.args[i] = interpretInstruction(std::get(instruction.args[i])); } // this is safe to do because we just evaluated the instruction Value argTypeVal = std::get(instruction.args[i]); if (argTypeVal.type != ValueTypes::Type) { if (argTypeVal.type == ValueTypes::Identifier && argTypeVal.getString().value() == "]") { break; } throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } ValueTypes argType = argTypeVal.getType().value(); // identifiers cannot be returned by anything if (std::holds_alternative(instruction.args[i + 1])) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } Value argNameVal = std::get(instruction.args[i + 1]); if (argNameVal.type != ValueTypes::Identifier) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } std::string argName = argNameVal.getString().value(); args.emplace_back(argName, argType); argAmount += 2; } // (code) (morecode) std::vector body; for (int i = argAmount + 3; i < instruction.args.size(); i++) { if (std::holds_alternative(instruction.args[i])) { body.push_back(std::get(instruction.args[i])); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } return Value(Function(returnType, args, body)); } // Preprocess instructions inside instructions for (auto &arg : instruction.args) { if (std::holds_alternative(arg)) { arg = interpretInstruction(std::get(arg)); } } // Instructions that don't require preprocessing go below this line // It is safe to use std::get(...) on all arguments // First, search for custom functions if (environment.contains(instruction.instruction)) { if (environment[instruction.instruction].type == ValueTypes::Function) { Function function = environment[instruction.instruction].getFunction().value(); if (function.arguments.size() != instruction.args.size()) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } Interpreter fnInterpreter; fnInterpreter.environment = this->environment; for (int i = 0; i < instruction.args.size(); i++) { Value argTypeVal = std::get(instruction.args[i]); if (argTypeVal.type != function.arguments[i].second) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } fnInterpreter.environment[function.arguments[i].first] = std::get(instruction.args[i]); } Value fnReturnValue; for (Instruction& inst : function.code) { if (inst.instruction == "return") { return fnInterpreter.interpretInstruction(inst); } fnReturnValue = fnInterpreter.interpretInstruction(inst); } return fnReturnValue; } else { return environment[instruction.instruction]; } return Value(); } if (instruction.instruction == "+" || instruction.instruction == "add") { double result = 0; for (const auto& arg : instruction.args) { auto value = std::get(arg); switch (value.type) { case ValueTypes::Int: { result += value.getInt().value(); break; } case ValueTypes::Double: { result += value.getDouble().value(); break; } default: { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } } if (result == static_cast(result)) { return Value(static_cast(result)); } return Value(result); } if (instruction.instruction == "-" || instruction.instruction == "subtract") { if (instruction.args.empty()) return Value(0); auto firstValue = std::get(instruction.args[0]); double result = 0; if (firstValue.type == ValueTypes::Int) { result = firstValue.getInt().value(); } else if (firstValue.type == ValueTypes::Double) { result = firstValue.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } for (size_t i = 1; i < instruction.args.size(); i++) { auto value = std::get(instruction.args[i]); if (value.type == ValueTypes::Int) { result -= value.getInt().value(); } else if (value.type == ValueTypes::Double) { result -= value.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } if (result == static_cast(result)) { return Value(static_cast(result)); } return Value(result); } if (instruction.instruction == "*" || instruction.instruction == "multiply") { if (instruction.args.empty()) return Value(1); auto firstValue = std::get(instruction.args[0]); double result = 0; if (firstValue.type == ValueTypes::Int) { result = firstValue.getInt().value(); } else if (firstValue.type == ValueTypes::Double) { result = firstValue.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } for (size_t i = 1; i < instruction.args.size(); i++) { auto value = std::get(instruction.args[i]); if (value.type == ValueTypes::Int) { result *= value.getInt().value(); } else if (value.type == ValueTypes::Double) { result *= value.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } if (result == static_cast(result)) { return Value(static_cast(result)); } return Value(result); } if (instruction.instruction == "/" || instruction.instruction == "divide") { if (instruction.args.empty()) return Value(1); auto firstValue = std::get(instruction.args[0]); double result = 0; if (firstValue.type == ValueTypes::Int) { result = firstValue.getInt().value(); } else if (firstValue.type == ValueTypes::Double) { result = firstValue.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } for (size_t i = 1; i < instruction.args.size(); i++) { auto value = std::get(instruction.args[i]); if (value.type == ValueTypes::Int) { if (value.getInt().value() == 0) { throw InterpretingError(InterpreterErrorType::MathError); } result /= value.getInt().value(); } else if (value.type == ValueTypes::Double) { if (value.getDouble().value() == 0) { throw InterpretingError(InterpreterErrorType::MathError); } result /= value.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } if (result == static_cast(result)) { return Value(static_cast(result)); } return Value(result); } if (instruction.instruction == "==" || instruction.instruction == "equal") { bool first = true; Value firstVal; for (const auto& arg : instruction.args) { auto value = std::get(arg); if (first) { first = false; firstVal = value; } else { if (value != firstVal) { return Value(); } } } return Value(1); } if (instruction.instruction == "!=" || instruction.instruction == "inequal") { bool first = true; Value firstVal; for (const auto& arg : instruction.args) { auto value = std::get(arg); if (first) { first = false; firstVal = value; } else { if (value == firstVal) { return Value(); } } } return Value(1); } if (instruction.instruction == "<" || instruction.instruction == "lesser") { if (instruction.args.size() != 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } auto left = std::get(instruction.args[0]); auto right = std::get(instruction.args[1]); double leftVal = 0, rightVal = 0; if (left.type == ValueTypes::Int) { leftVal = left.getInt().value(); } else if (left.type == ValueTypes::Double) { leftVal = left.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } if (right.type == ValueTypes::Int) { rightVal = right.getInt().value(); } else if (right.type == ValueTypes::Double) { rightVal = right.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } return leftVal < rightVal ? Value(1) : Value(); } if (instruction.instruction == ">" || instruction.instruction == "greater") { if (instruction.args.size() != 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } auto left = std::get(instruction.args[0]); auto right = std::get(instruction.args[1]); double leftVal = 0, rightVal = 0; if (left.type == ValueTypes::Int) { leftVal = left.getInt().value(); } else if (left.type == ValueTypes::Double) { leftVal = left.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } if (right.type == ValueTypes::Int) { rightVal = right.getInt().value(); } else if (right.type == ValueTypes::Double) { rightVal = right.getDouble().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } return leftVal > rightVal ? Value(1) : Value(); } if (instruction.instruction == "input") { std::string input; linenoise::Readline("", input); return Value(input); } if (instruction.instruction == "return") { if (instruction.args.empty()) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } return std::get(instruction.args[0]); } if (instruction.instruction == "list") { std::vector list; for (const auto& item : instruction.args) { list.push_back(std::get(item)); } return Value(list); } if (instruction.instruction == "let") { if (instruction.args.size() < 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } auto idval = std::get(instruction.args[0]); std::string id; if (idval.type == ValueTypes::Identifier) { id = idval.getString().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } environment[id] = std::get(instruction.args[1]); return {environment[id]}; } if (instruction.instruction == "set") { if (instruction.args.size() < 2) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } auto idval = std::get(instruction.args[0]); std::string id; if (idval.type == ValueTypes::Identifier) { id = idval.getString().value(); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } if (!environment.contains(id)) { throw InterpretingError(InterpreterErrorType::UnknownEnvironment); } environment[id] = std::get(instruction.args[1]); return {environment[id]}; } if (instruction.instruction == "print") { for (const auto& arg : instruction.args) { auto value = std::get(arg); value.print(); std::cout << " "; } std::cout << "\n"; return Value(); } if (instruction.instruction == "log") { for (const auto& arg : instruction.args) { std::cout << "\033[2;3;93m"; auto value = std::get(arg); value.print(); std::cout << " "; } std::cout << "\033[0m\n"; return Value(); } if (instruction.instruction == "exit") { if (!instruction.args.empty()) { auto value = std::get(instruction.args[0]); if (value.type == ValueTypes::Int) { exit(value.getInt().value()); } } exit(0); } throw InterpretingError(InterpreterErrorType::UnknownInstruction); } explicit Interpreter(std::vector in) : instructions(std::move(in)) { for (Instruction &instruction : instructions) { if (instruction.instruction == "return") { if (instruction.args.empty()) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } if (std::holds_alternative(instruction.args[0])) { instruction.args[0] = interpretInstruction(std::get(instruction.args[0])); } returnValue = std::get(instruction.args[0]); return; } returnValue = interpretInstruction(instruction); } } Interpreter() = default; }; class CppTranspiler { std::vector instructions; std::map functions; std::map environment; std::vector headers; std::vector body; std::vector tail; int fnAmount = 0; void validateFunctionCall(const std::string& name, const std::vector>& args) { // Check if function exists if (functions.find(name) == functions.end()) { throw CTranspilerError(CTranspilerErrorType::UnknownEnvironment); } Function& func = functions[name]; // Check argument count if (args.size() != func.arguments.size()) { std::cerr << "Function '" << name << "' expects " << func.arguments.size() << " arguments but got " << args.size() << std::endl; throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } // Check argument types (if we know them at compile time) for (size_t i = 0; i < args.size(); i++) { if (std::holds_alternative(args[i])) { Value argVal = std::get(args[i]); ValueTypes expectedType = func.arguments[i].second; // Only validate if it's a literal value (not an identifier or expression) if (argVal.type != ValueTypes::Identifier && argVal.type != expectedType) { std::cerr << "Function '" << name << "' argument " << i << " ('" << func.arguments[i].first << "') expects type " << static_cast(expectedType) << " but got " << static_cast(argVal.type) << std::endl; throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } } } } std::string compileFunction(const Instruction &instruction) { std::stringstream functionss; Function function; auto returnTypeValue = std::get(instruction.args[0]); if (returnTypeValue.type != ValueTypes::Type) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } ValueTypes returnType = returnTypeValue.getType().value(); std::vector> args; int argAmount = 0; if (std::holds_alternative(instruction.args[1]) && std::get(instruction.args[1]).type == ValueTypes::Identifier && std::get(instruction.args[1]).getString().value() != "[") { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } // Parse arguments for (int i = 2; i < instruction.args.size(); i += 2) { Value argTypeVal = std::get(instruction.args[i]); if (argTypeVal.type != ValueTypes::Type) { if (argTypeVal.type == ValueTypes::Identifier && argTypeVal.getString().value() == "]") { break; } throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } ValueTypes argType = argTypeVal.getType().value(); if (std::holds_alternative(instruction.args[i + 1])) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } Value argNameVal = std::get(instruction.args[i + 1]); if (argNameVal.type != ValueTypes::Identifier) { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } std::string argName = argNameVal.getString().value(); args.emplace_back(argName, argType); argAmount += 2; } std::vector body; for (int i = argAmount + 3; i < instruction.args.size(); i++) { if (std::holds_alternative(instruction.args[i])) { body.push_back(std::get(instruction.args[i])); } else { throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } // Store in function object function.returnType = returnType; function.arguments = args; function.code = body; // Generate C++ code std::string returntype; switch (returnType) { case ValueTypes::Int: returntype = "int"; break; case ValueTypes::Double: returntype = "double"; break; case ValueTypes::String: returntype = "std::string"; break; default: std::cout << "that's not currently supported" << std::endl; throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } bool first = true; std::stringstream argss; for (const auto& arg : function.arguments) { if (!first) { argss << ", "; } first = false; switch (arg.second) { case ValueTypes::Int: argss << "int " << arg.first; break; case ValueTypes::Double: argss << "double " << arg.first; break; case ValueTypes::String: argss << "std::string " << arg.first; break; default: std::cout << "that's not currently supported" << std::endl; throw InterpretingError(InterpreterErrorType::IncorrectTokenType); } } functionss << "[](" << argss.str() << ") -> " << returntype << " {\n"; for (const auto& inst : function.code) { functionss << " " << compileInstruction(inst) << ";\n"; } functionss << "}"; return functionss.str(); } std::string compileInstruction(const Instruction &instruction) { // the value is for values being held, the std::string is for compiled instructions std::vector> args; for (const auto &arg : instruction.args) { if (std::holds_alternative(arg)) { args.emplace_back("(" + compileInstruction(std::get(arg)) + ")"); } if (std::holds_alternative(arg)) { args.emplace_back(std::get(arg)); } } if (functions.find(instruction.instruction) != functions.end()) { // Validate the call validateFunctionCall(instruction.instruction, args); // Compile the call std::stringstream call; call << instruction.instruction << "("; bool first = true; for (const auto& arg : args) { if (!first) call << ", "; first = false; if (std::holds_alternative(arg)) { call << std::get(arg); } else { Value v = std::get(arg); if (v.type == ValueTypes::String) { call << "\"" << v.getString().value() << "\""; } else if (v.type == ValueTypes::Int) { call << v.getInt().value(); } else if (v.type == ValueTypes::Double) { call << v.getDouble().value(); } else if (v.type == ValueTypes::Identifier) { call << v.getString().value(); } } } call << ")"; return call.str(); } if (instruction.instruction == "let") { if (args.size() < 2) { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } if (!std::holds_alternative(args[0])) { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } Value nameVal = std::get(args[0]); if (nameVal.type != ValueTypes::Identifier) { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } std::string varName = nameVal.getString().value(); // Check if it's a function definition if (std::holds_alternative(args[1])) { std::string compiled = std::get(args[1]); // Check if the original instruction.args[1] was a function if (std::holds_alternative(instruction.args[1])) { Instruction funcInst = std::get(instruction.args[1]); if (funcInst.instruction == "function") { // Extract the function signature and store it Function func; auto returnTypeValue = std::get(funcInst.args[0]); func.returnType = returnTypeValue.getType().value(); // Parse arguments int argAmount = 0; for (int i = 2; i < funcInst.args.size(); i += 2) { Value argTypeVal = std::get(funcInst.args[i]); if (argTypeVal.type != ValueTypes::Type) { if (argTypeVal.type == ValueTypes::Identifier && argTypeVal.getString().value() == "]") { break; } break; } ValueTypes argType = argTypeVal.getType().value(); Value argNameVal = std::get(funcInst.args[i + 1]); std::string argName = argNameVal.getString().value(); func.arguments.emplace_back(argName, argType); argAmount += 2; } // Store the function signature functions[varName] = func; } } return "auto " + varName + " = " + compiled; } std::stringstream code; code << "auto "; if (std::holds_alternative(args[0]) && std::get(args[0]).type == ValueTypes::Identifier) { code << std::get(args[0]).getString().value() << " = "; } else { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } if (std::holds_alternative(args[1])) { code << std::get(args[1]); } if (std::holds_alternative(args[1])) { code << std::get(args[1]); } return code.str(); } if (instruction.instruction == "function") { if (std::ranges::find(headers, "functional") == headers.end()) { headers.emplace_back("functional"); } return compileFunction(instruction); } if (instruction.instruction == "return") { if (!args.empty()) { if (std::holds_alternative(args[0])) { Value value = std::get(args[0]); if (value.type == ValueTypes::Int) { return "return " + std::to_string(value.getInt().value()); } else { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } } if (std::holds_alternative(args[0])) { return "return(" + std::get(args[0]) + ")"; } } else { throw CTranspilerError(CTranspilerErrorType::IncorrectTokenType); } } if (instruction.instruction == "print") { std::stringstream retval; if (std::ranges::find(headers, "iostream") == headers.end()) { headers.emplace_back("iostream"); } for (const auto &arg : args) { if (std::holds_alternative(arg)) { retval << "std::cout << " << std::get(arg) << " << \"\\n\""; } if (std::holds_alternative(arg)) { if (std::get(arg).type == ValueTypes::String) { retval << "std::cout << \"" << std::get(arg) << "\" << \"\\n\""; } else { retval << "std::cout << " << std::get(arg) << " << \"\\n\""; } } } return retval.str(); } throw InterpretingError(InterpreterErrorType::UnknownInstruction); } public: std::string createCppFile() const { std::stringstream cpp; cpp << "// Autogenerated by the Pipple compiler.\n// Headers\n"; for (const auto &header : headers) { cpp << "#include <" << header << ">\n"; } cpp << "\n// Code\n"; for (const auto &body : body) { cpp << body << "\n"; } for (const auto &tail : tail) { cpp << tail << "\n"; } return cpp.str(); } void addInstruction(const Instruction &instruction) { body.emplace_back(compileInstruction(instruction) + ";"); } explicit CppTranspiler(std::vector in) : instructions(std::move(in)) { for (Instruction &instruction : instructions) { body.push_back(compileInstruction(instruction)); } std::cout << createCppFile() << std::endl; } CppTranspiler() { headers = {}; body = { "void* nil = nullptr;", "int main(int argc, char** argv) {", }; tail = { "}" }; } }; int main(int argc, char** argv) { bool isInteractive = true; bool compiling = false; std::string program; if (argc > 1) { int fileNum = 1; if (strcmp(argv[1], "-c") == 0) { fileNum = 2; compiling = true; } isInteractive = false; std::ifstream ifs(argv[fileNum]); std::stringstream buffer; if (ifs.is_open()) { buffer << ifs.rdbuf(); } program = buffer.str(); } linenoise::SetMultiLine(true); linenoise::SetHistoryMaxLen(50); Parser parser; Interpreter interpreter; CppTranspiler transpiler; while (true) { if (isInteractive) { if (linenoise::Readline("pipple> ", program)) { return 0; } } try { parser = Parser(program); } catch (const ParsingError& e) { std::cerr << "Parse error: " << e.what() << std::endl; // Show code context const std::string& source = e.getSourceCode(); if (!source.empty()) { int pos = e.getPosition(); // Calculate character position in source from token position int charPos = 0; int tokenCount = 0; bool inString = false; for (size_t i = 0; i < source.length() && tokenCount < pos; i++) { char c = source[i]; if (c == '"') inString = !inString; if (!inString && (c == '(' || c == ')' || c == ' ' || c == '\n')) { if (i > 0 && source[i-1] != ' ' && source[i-1] != '\n' && source[i-1] != '(' && source[i-1] != ')') { tokenCount++; } if (c == '(' || c == ')') tokenCount++; } charPos = static_cast(i); } // Show context (30 chars before and after) int start = std::max(0, charPos - 30); int end = std::min(static_cast(source.length()), charPos + 30); std::cerr << "\nCode context:\n"; if (start > 0) std::cerr << "..."; std::cerr << source.substr(start, end - start); if (end < static_cast(source.length())) std::cerr << "..."; std::cerr << "\n"; // Show pointer to error position int pointerPos = (start > 0 ? 3 : 0) + (charPos - start); std::cerr << std::string(pointerPos, ' ') << "^\n"; } if (!isInteractive) return 1; continue; } catch (const std::exception& e) { std::cerr << "Unexpected error: " << e.what() << std::endl; if (!isInteractive) return 1; } try { if (compiling) { for (const auto &instruction : parser.instructions) { transpiler.addInstruction(instruction); } } else { for (auto instruction : parser.instructions) { interpreter.interpretInstruction(instruction); } } } catch (const InterpretingError& e) { std::cerr << e.what() << std::endl; if (!isInteractive) return 1; } if (!isInteractive) { break; } } if (compiling) { std::cout << transpiler.createCppFile() << std::endl; } return 0; }