From d782f6da02e487eb451aaee6bf526f0537784fab Mon Sep 17 00:00:00 2001 From: "clank-hayen[bot]" Date: Fri, 2 Jan 2026 02:54:58 +0000 Subject: [PATCH 1/4] feat: add Deque, Stack, Queue collection types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement three new collection types as specified in issue #38: - Deque: Double-ended queue with push_front, push_back, pop_front, pop_back, front, back, get, length, is_empty, clear - Stack: LIFO stack with push, pop, top, length, is_empty - Queue: FIFO queue with push, pop, front, back, length, is_empty Changes: - Add DEQUE, STACK, QUEUE tokens to lexer - Add DequeCreate, StackCreate, QueueCreate AST nodes - Add parsing support in parse_primary.cpp - Add type checking with method validation - Add code generation mapping to std::deque, std::stack, std::queue - Add comprehensive tests for all collections - Update README with documentation Closes #38 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CMakeLists.txt | 9 ++ README.md | 112 +++++++++++++++++++ codegen/codegen.hpp | 12 ++ codegen/emit_deque.cpp | 77 +++++++++++++ codegen/emit_expression.cpp | 12 ++ codegen/emit_method_call.cpp | 15 +++ codegen/emit_queue.cpp | 58 ++++++++++ codegen/emit_stack.cpp | 54 +++++++++ codegen/emit_type.cpp | 21 ++++ lexer/lexer.cpp | 3 + lexer/token.hpp | 3 + parser/ast.hpp | 15 +++ parser/parse_primary.cpp | 57 ++++++++++ tests/test_deque.b | 177 ++++++++++++++++++++++++++++++ tests/test_queue.b | 150 +++++++++++++++++++++++++ tests/test_stack.b | 143 ++++++++++++++++++++++++ typechecker/check_deque.cpp | 61 ++++++++++ typechecker/check_expression.cpp | 12 ++ typechecker/check_method_call.cpp | 33 ++++++ typechecker/check_queue.cpp | 61 ++++++++++ typechecker/check_stack.cpp | 61 ++++++++++ typechecker/deques.cpp | 153 ++++++++++++++++++++++++++ typechecker/deques.hpp | 29 +++++ typechecker/queues.cpp | 105 ++++++++++++++++++ typechecker/queues.hpp | 29 +++++ typechecker/stacks.cpp | 93 ++++++++++++++++ typechecker/stacks.hpp | 29 +++++ typechecker/typechecker.hpp | 12 ++ 28 files changed, 1596 insertions(+) create mode 100644 codegen/emit_deque.cpp create mode 100644 codegen/emit_queue.cpp create mode 100644 codegen/emit_stack.cpp create mode 100644 tests/test_deque.b create mode 100644 tests/test_queue.b create mode 100644 tests/test_stack.b create mode 100644 typechecker/check_deque.cpp create mode 100644 typechecker/check_queue.cpp create mode 100644 typechecker/check_stack.cpp create mode 100644 typechecker/deques.cpp create mode 100644 typechecker/deques.hpp create mode 100644 typechecker/queues.cpp create mode 100644 typechecker/queues.hpp create mode 100644 typechecker/stacks.cpp create mode 100644 typechecker/stacks.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b162c0b..591b97f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,8 +92,14 @@ add_library(bishop_lib typechecker/lists.cpp typechecker/pairs.cpp typechecker/tuples.cpp + typechecker/deques.cpp + typechecker/stacks.cpp + typechecker/queues.cpp typechecker/check_pair.cpp typechecker/check_tuple.cpp + typechecker/check_deque.cpp + typechecker/check_stack.cpp + typechecker/check_queue.cpp typechecker/check_lambda.cpp codegen/codegen.cpp codegen/emit_type.cpp @@ -124,6 +130,9 @@ add_library(bishop_lib codegen/emit_string.cpp codegen/emit_pair.cpp codegen/emit_tuple.cpp + codegen/emit_deque.cpp + codegen/emit_stack.cpp + codegen/emit_queue.cpp codegen/emit_method_call.cpp codegen/emit_function_call.cpp codegen/emit_field.cpp diff --git a/README.md b/README.md index 5c1c19d..6904403 100644 --- a/README.md +++ b/README.md @@ -677,6 +677,118 @@ for i in 0..5 { // sum is 15 ``` +## Deques + +A double-ended queue that supports efficient insertion and removal from both ends. + +### Deque Creation + +```bishop +dq := Deque(); // Empty deque +dq := Deque(); // Empty string deque +``` + +### Deque Methods + +| Method | Description | +|------------------|--------------------------------------| +| `push_front(T)` | Add element to front | +| `push_back(T)` | Add element to back | +| `pop_front()` | Remove and return front element | +| `pop_back()` | Remove and return back element | +| `front()` | Return front element without removing| +| `back()` | Return back element without removing | +| `get(int)` | Access element at index | +| `length()` | Return number of elements | +| `is_empty()` | Return true if deque is empty | +| `clear()` | Remove all elements | + +### Deque Usage + +```bishop +dq := Deque(); +dq.push_back(1); // [1] +dq.push_back(2); // [1, 2] +dq.push_front(0); // [0, 1, 2] + +val := dq.pop_front(); // val = 0, dq = [1, 2] +val = dq.pop_back(); // val = 2, dq = [1] + +print(dq.front()); // 1 +print(dq.length()); // 1 +``` + +## Stacks + +A LIFO (Last-In-First-Out) stack data structure. + +### Stack Creation + +```bishop +s := Stack(); // Empty stack +s := Stack(); // Empty string stack +``` + +### Stack Methods + +| Method | Description | +|---------------|-------------------------------------| +| `push(T)` | Push element onto top of stack | +| `pop()` | Remove and return top element (LIFO)| +| `top()` | Return top element without removing | +| `length()` | Return number of elements | +| `is_empty()` | Return true if stack is empty | + +### Stack Usage (LIFO Order) + +```bishop +s := Stack(); +s.push(1); +s.push(2); +s.push(3); + +print(s.top()); // 3 (top element) +print(s.pop()); // 3 (removed, LIFO) +print(s.pop()); // 2 +print(s.pop()); // 1 +``` + +## Queues + +A FIFO (First-In-First-Out) queue data structure. + +### Queue Creation + +```bishop +q := Queue(); // Empty queue +q := Queue(); // Empty string queue +``` + +### Queue Methods + +| Method | Description | +|---------------|--------------------------------------| +| `push(T)` | Add element to back of queue | +| `pop()` | Remove and return front element (FIFO)| +| `front()` | Return front element without removing| +| `back()` | Return back element without removing | +| `length()` | Return number of elements | +| `is_empty()` | Return true if queue is empty | + +### Queue Usage (FIFO Order) + +```bishop +q := Queue(); +q.push(1); +q.push(2); +q.push(3); + +print(q.front()); // 1 (first in) +print(q.pop()); // 1 (removed, FIFO) +print(q.pop()); // 2 +print(q.pop()); // 3 +``` + ## Error Handling ### Error Types diff --git a/codegen/codegen.hpp b/codegen/codegen.hpp index 6653c2b..6084250 100644 --- a/codegen/codegen.hpp +++ b/codegen/codegen.hpp @@ -113,6 +113,18 @@ std::string emit_pair_field_access(const std::string& obj_str, const std::string std::string emit_tuple_create(CodeGenState& state, const TupleCreate& tuple); std::string emit_tuple_method_call(CodeGenState& state, const MethodCall& call, const std::string& obj_str, const std::vector& args); +// Deque (emit_deque.cpp) +std::string emit_deque_create(const DequeCreate& deque); +std::string emit_deque_method_call(CodeGenState& state, const MethodCall& call, const std::string& obj_str, const std::vector& args); + +// Stack (emit_stack.cpp) +std::string emit_stack_create(const StackCreate& stack); +std::string emit_stack_method_call(CodeGenState& state, const MethodCall& call, const std::string& obj_str, const std::vector& args); + +// Queue (emit_queue.cpp) +std::string emit_queue_create(const QueueCreate& queue); +std::string emit_queue_method_call(CodeGenState& state, const MethodCall& call, const std::string& obj_str, const std::vector& args); + // Method call (emit_method_call.cpp) std::string method_call(const std::string& object, const std::string& method, const std::vector& args); std::string emit_method_call(CodeGenState& state, const MethodCall& call); diff --git a/codegen/emit_deque.cpp b/codegen/emit_deque.cpp new file mode 100644 index 0000000..052e07c --- /dev/null +++ b/codegen/emit_deque.cpp @@ -0,0 +1,77 @@ +/** + * @file emit_deque.cpp + * @brief Deque emission for the Bishop code generator. + */ + +#include "codegen.hpp" +#include + +using namespace std; + +namespace codegen { + +/** + * Emits a deque creation: Deque() -> std::deque{}. + */ +string emit_deque_create(const DequeCreate& deque) { + string cpp_type = map_type(deque.element_type); + return "std::deque<" + cpp_type + ">{}"; +} + +/** + * Emits a deque method call, mapping Bishop methods to std::deque equivalents. + */ +string emit_deque_method_call(CodeGenState& state, const MethodCall& call, const string& obj_str, const vector& args) { + (void)state; + + if (call.method_name == "length") { + return obj_str + ".size()"; + } + + if (call.method_name == "is_empty") { + return obj_str + ".empty()"; + } + + if (call.method_name == "push_front") { + return obj_str + ".push_front(" + args[0] + ")"; + } + + if (call.method_name == "push_back") { + return obj_str + ".push_back(" + args[0] + ")"; + } + + if (call.method_name == "pop_front") { + return fmt::format( + "[](auto& d) {{ auto tmp = d.front(); d.pop_front(); return tmp; }}({})", + obj_str + ); + } + + if (call.method_name == "pop_back") { + return fmt::format( + "[](auto& d) {{ auto tmp = d.back(); d.pop_back(); return tmp; }}({})", + obj_str + ); + } + + if (call.method_name == "front") { + return obj_str + ".front()"; + } + + if (call.method_name == "back") { + return obj_str + ".back()"; + } + + if (call.method_name == "get") { + return obj_str + ".at(" + args[0] + ")"; + } + + if (call.method_name == "clear") { + return obj_str + ".clear()"; + } + + // Unknown deque method - fall back to generic method call + return method_call(obj_str, call.method_name, args); +} + +} // namespace codegen diff --git a/codegen/emit_expression.cpp b/codegen/emit_expression.cpp index f416932..10b97dc 100644 --- a/codegen/emit_expression.cpp +++ b/codegen/emit_expression.cpp @@ -106,6 +106,18 @@ string emit(CodeGenState& state, const ASTNode& node) { return emit_tuple_create(state, *tuple); } + if (auto* deque = dynamic_cast(&node)) { + return emit_deque_create(*deque); + } + + if (auto* stack = dynamic_cast(&node)) { + return emit_stack_create(*stack); + } + + if (auto* queue = dynamic_cast(&node)) { + return emit_queue_create(*queue); + } + if (auto* lambda = dynamic_cast(&node)) { return emit_lambda_expr(state, *lambda); } diff --git a/codegen/emit_method_call.cpp b/codegen/emit_method_call.cpp index cc2051e..2cc7b32 100644 --- a/codegen/emit_method_call.cpp +++ b/codegen/emit_method_call.cpp @@ -113,6 +113,21 @@ string emit_method_call(CodeGenState& state, const MethodCall& call) { return emit_tuple_method_call(state, call, obj_str, args); } + // Handle Deque methods + if (call.object_type.rfind("Deque<", 0) == 0) { + return emit_deque_method_call(state, call, obj_str, args); + } + + // Handle Stack methods + if (call.object_type.rfind("Stack<", 0) == 0) { + return emit_stack_method_call(state, call, obj_str, args); + } + + // Handle Queue methods + if (call.object_type.rfind("Queue<", 0) == 0) { + return emit_queue_method_call(state, call, obj_str, args); + } + // Use -> for pointer types (auto-deref like Go) if (!call.object_type.empty() && call.object_type.back() == '*') { return fmt::format("{}->{}({})", obj_str, call.method_name, fmt::join(args, ", ")); diff --git a/codegen/emit_queue.cpp b/codegen/emit_queue.cpp new file mode 100644 index 0000000..456e8a5 --- /dev/null +++ b/codegen/emit_queue.cpp @@ -0,0 +1,58 @@ +/** + * @file emit_queue.cpp + * @brief Queue emission for the Bishop code generator. + */ + +#include "codegen.hpp" +#include + +using namespace std; + +namespace codegen { + +/** + * Emits a queue creation: Queue() -> std::queue{}. + */ +string emit_queue_create(const QueueCreate& queue) { + string cpp_type = map_type(queue.element_type); + return "std::queue<" + cpp_type + ">{}"; +} + +/** + * Emits a queue method call, mapping Bishop methods to std::queue equivalents. + */ +string emit_queue_method_call(CodeGenState& state, const MethodCall& call, const string& obj_str, const vector& args) { + (void)state; + + if (call.method_name == "length") { + return obj_str + ".size()"; + } + + if (call.method_name == "is_empty") { + return obj_str + ".empty()"; + } + + if (call.method_name == "push") { + return obj_str + ".push(" + args[0] + ")"; + } + + if (call.method_name == "pop") { + return fmt::format( + "[](auto& q) {{ auto tmp = q.front(); q.pop(); return tmp; }}({})", + obj_str + ); + } + + if (call.method_name == "front") { + return obj_str + ".front()"; + } + + if (call.method_name == "back") { + return obj_str + ".back()"; + } + + // Unknown queue method - fall back to generic method call + return method_call(obj_str, call.method_name, args); +} + +} // namespace codegen diff --git a/codegen/emit_stack.cpp b/codegen/emit_stack.cpp new file mode 100644 index 0000000..af49932 --- /dev/null +++ b/codegen/emit_stack.cpp @@ -0,0 +1,54 @@ +/** + * @file emit_stack.cpp + * @brief Stack emission for the Bishop code generator. + */ + +#include "codegen.hpp" +#include + +using namespace std; + +namespace codegen { + +/** + * Emits a stack creation: Stack() -> std::stack{}. + */ +string emit_stack_create(const StackCreate& stack) { + string cpp_type = map_type(stack.element_type); + return "std::stack<" + cpp_type + ">{}"; +} + +/** + * Emits a stack method call, mapping Bishop methods to std::stack equivalents. + */ +string emit_stack_method_call(CodeGenState& state, const MethodCall& call, const string& obj_str, const vector& args) { + (void)state; + + if (call.method_name == "length") { + return obj_str + ".size()"; + } + + if (call.method_name == "is_empty") { + return obj_str + ".empty()"; + } + + if (call.method_name == "push") { + return obj_str + ".push(" + args[0] + ")"; + } + + if (call.method_name == "pop") { + return fmt::format( + "[](auto& s) {{ auto tmp = s.top(); s.pop(); return tmp; }}({})", + obj_str + ); + } + + if (call.method_name == "top") { + return obj_str + ".top()"; + } + + // Unknown stack method - fall back to generic method call + return method_call(obj_str, call.method_name, args); +} + +} // namespace codegen diff --git a/codegen/emit_type.cpp b/codegen/emit_type.cpp index ff74b76..0a43146 100644 --- a/codegen/emit_type.cpp +++ b/codegen/emit_type.cpp @@ -78,6 +78,27 @@ string map_type(const string& t) { return "std::vector<" + map_type(element_type) + ">"; } + // Handle Deque types: Deque -> std::deque + if (t.rfind("Deque<", 0) == 0 && t.back() == '>') { + string element_type = extract_element_type(t, "Deque<"); + assert(!element_type.empty() && "malformed Deque type passed typechecker"); + return "std::deque<" + map_type(element_type) + ">"; + } + + // Handle Stack types: Stack -> std::stack + if (t.rfind("Stack<", 0) == 0 && t.back() == '>') { + string element_type = extract_element_type(t, "Stack<"); + assert(!element_type.empty() && "malformed Stack type passed typechecker"); + return "std::stack<" + map_type(element_type) + ">"; + } + + // Handle Queue types: Queue -> std::queue + if (t.rfind("Queue<", 0) == 0 && t.back() == '>') { + string element_type = extract_element_type(t, "Queue<"); + assert(!element_type.empty() && "malformed Queue type passed typechecker"); + return "std::queue<" + map_type(element_type) + ">"; + } + // Handle function types: fn(int, str) -> bool -> std::function if (t.rfind("fn(", 0) == 0) { // Find the closing paren and extract param types diff --git a/lexer/lexer.cpp b/lexer/lexer.cpp index f771992..67324c6 100644 --- a/lexer/lexer.cpp +++ b/lexer/lexer.cpp @@ -36,6 +36,9 @@ static unordered_map keywords = { {"List", TokenType::LIST}, {"Pair", TokenType::PAIR}, {"Tuple", TokenType::TUPLE}, + {"Deque", TokenType::DEQUE}, + {"Stack", TokenType::STACK}, + {"Queue", TokenType::QUEUE}, {"select", TokenType::SELECT}, {"case", TokenType::CASE}, {"extern", TokenType::EXTERN}, diff --git a/lexer/token.hpp b/lexer/token.hpp index e47aef8..a6fc540 100644 --- a/lexer/token.hpp +++ b/lexer/token.hpp @@ -161,6 +161,9 @@ enum class TokenType { LIST, PAIR, TUPLE, + DEQUE, + STACK, + QUEUE, SELECT, CASE, EXTERN, diff --git a/parser/ast.hpp b/parser/ast.hpp index f6dcdff..c40a795 100644 --- a/parser/ast.hpp +++ b/parser/ast.hpp @@ -197,6 +197,21 @@ struct TupleCreate : ASTNode { vector> elements; ///< Element expressions (2-5 elements) }; +/** @brief Deque creation: Deque() */ +struct DequeCreate : ASTNode { + string element_type; ///< Type of elements the deque holds +}; + +/** @brief Stack creation: Stack() */ +struct StackCreate : ASTNode { + string element_type; ///< Type of elements the stack holds +}; + +/** @brief Queue creation: Queue() */ +struct QueueCreate : ASTNode { + string element_type; ///< Type of elements the queue holds +}; + /** @brief A single case in a select statement */ struct SelectCase : ASTNode { string binding_name; ///< Variable to bind result (empty for send) diff --git a/parser/parse_primary.cpp b/parser/parse_primary.cpp index 18ac2ae..8397bd8 100644 --- a/parser/parse_primary.cpp +++ b/parser/parse_primary.cpp @@ -220,6 +220,63 @@ unique_ptr parse_primary(ParserState& state) { return tuple; } + // Handle deque creation: Deque() or Deque>() + if (check(state, TokenType::DEQUE)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Deque> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + consume(state, TokenType::LPAREN); + consume(state, TokenType::RPAREN); + + auto deque = make_unique(); + deque->element_type = element_type; + deque->line = start_line; + return deque; + } + + // Handle stack creation: Stack() or Stack>() + if (check(state, TokenType::STACK)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Stack> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + consume(state, TokenType::LPAREN); + consume(state, TokenType::RPAREN); + + auto stack = make_unique(); + stack->element_type = element_type; + stack->line = start_line; + return stack; + } + + // Handle queue creation: Queue() or Queue>() + if (check(state, TokenType::QUEUE)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Queue> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + consume(state, TokenType::LPAREN); + consume(state, TokenType::RPAREN); + + auto queue = make_unique(); + queue->element_type = element_type; + queue->line = start_line; + return queue; + } + // Handle list literal: [expr, expr, ...] if (check(state, TokenType::LBRACKET)) { int start_line = current(state).line; diff --git a/tests/test_deque.b b/tests/test_deque.b new file mode 100644 index 0000000..54847cc --- /dev/null +++ b/tests/test_deque.b @@ -0,0 +1,177 @@ +// ============================================ +// Deque Creation +// ============================================ + +fn test_deque_create_empty_int() { + dq := Deque(); + assert_eq(dq.length(), 0); +} + +fn test_deque_create_empty_str() { + dq := Deque(); + assert_eq(dq.is_empty(), true); +} + +// ============================================ +// Deque Push Operations +// ============================================ + +fn test_deque_push_front() { + dq := Deque(); + dq.push_front(1); + dq.push_front(2); + dq.push_front(3); + assert_eq(dq.length(), 3); + assert_eq(dq.front(), 3); +} + +fn test_deque_push_back() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + assert_eq(dq.length(), 3); + assert_eq(dq.back(), 3); +} + +fn test_deque_push_front_and_back() { + dq := Deque(); + dq.push_back(2); + dq.push_front(1); + dq.push_back(3); + assert_eq(dq.front(), 1); + assert_eq(dq.back(), 3); + assert_eq(dq.length(), 3); +} + +// ============================================ +// Deque Pop Operations +// ============================================ + +fn test_deque_pop_front() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + val := dq.pop_front(); + assert_eq(val, 1); + assert_eq(dq.length(), 2); +} + +fn test_deque_pop_back() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + val := dq.pop_back(); + assert_eq(val, 3); + assert_eq(dq.length(), 2); +} + +fn test_deque_pop_front_and_back() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + front := dq.pop_front(); + back := dq.pop_back(); + assert_eq(front, 1); + assert_eq(back, 3); + assert_eq(dq.length(), 1); + assert_eq(dq.front(), 2); +} + +// ============================================ +// Deque Access Methods +// ============================================ + +fn test_deque_front() { + dq := Deque(); + dq.push_back(10); + dq.push_back(20); + assert_eq(dq.front(), 10); +} + +fn test_deque_back() { + dq := Deque(); + dq.push_back(10); + dq.push_back(20); + assert_eq(dq.back(), 20); +} + +fn test_deque_get() { + dq := Deque(); + dq.push_back(10); + dq.push_back(20); + dq.push_back(30); + assert_eq(dq.get(0), 10); + assert_eq(dq.get(1), 20); + assert_eq(dq.get(2), 30); +} + +// ============================================ +// Deque Query Methods +// ============================================ + +fn test_deque_length() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + assert_eq(dq.length(), 3); +} + +fn test_deque_is_empty_true() { + dq := Deque(); + assert_eq(dq.is_empty(), true); +} + +fn test_deque_is_empty_false() { + dq := Deque(); + dq.push_back(1); + assert_eq(dq.is_empty(), false); +} + +// ============================================ +// Deque Clear +// ============================================ + +fn test_deque_clear() { + dq := Deque(); + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.clear(); + assert_eq(dq.length(), 0); + assert_eq(dq.is_empty(), true); +} + +// ============================================ +// Deque with Strings +// ============================================ + +fn test_deque_str_push_front() { + dq := Deque(); + dq.push_front("a"); + dq.push_front("b"); + assert_eq(dq.front(), "b"); + assert_eq(dq.back(), "a"); +} + +fn test_deque_str_push_back() { + dq := Deque(); + dq.push_back("hello"); + dq.push_back("world"); + assert_eq(dq.front(), "hello"); + assert_eq(dq.back(), "world"); +} + +// ============================================ +// Typed Variable Declaration +// ============================================ + +fn test_deque_typed_decl() { + Deque dq = Deque(); + dq.push_back(1); + assert_eq(dq.length(), 1); +} diff --git a/tests/test_queue.b b/tests/test_queue.b new file mode 100644 index 0000000..2ae93df --- /dev/null +++ b/tests/test_queue.b @@ -0,0 +1,150 @@ +// ============================================ +// Queue Creation +// ============================================ + +fn test_queue_create_empty_int() { + q := Queue(); + assert_eq(q.length(), 0); +} + +fn test_queue_create_empty_str() { + q := Queue(); + assert_eq(q.is_empty(), true); +} + +// ============================================ +// Queue Push +// ============================================ + +fn test_queue_push() { + q := Queue(); + q.push(1); + q.push(2); + q.push(3); + assert_eq(q.length(), 3); +} + +fn test_queue_push_str() { + q := Queue(); + q.push("hello"); + q.push("world"); + assert_eq(q.length(), 2); +} + +// ============================================ +// Queue Pop (FIFO) +// ============================================ + +fn test_queue_pop() { + q := Queue(); + q.push(1); + q.push(2); + q.push(3); + val := q.pop(); + assert_eq(val, 1); + assert_eq(q.length(), 2); +} + +fn test_queue_pop_multiple() { + q := Queue(); + q.push(1); + q.push(2); + q.push(3); + first := q.pop(); + second := q.pop(); + third := q.pop(); + assert_eq(first, 1); + assert_eq(second, 2); + assert_eq(third, 3); +} + +fn test_queue_fifo_order() { + q := Queue(); + q.push("a"); + q.push("b"); + q.push("c"); + assert_eq(q.pop(), "a"); + assert_eq(q.pop(), "b"); + assert_eq(q.pop(), "c"); +} + +// ============================================ +// Queue Front and Back +// ============================================ + +fn test_queue_front() { + q := Queue(); + q.push(10); + q.push(20); + q.push(30); + assert_eq(q.front(), 10); +} + +fn test_queue_back() { + q := Queue(); + q.push(10); + q.push(20); + q.push(30); + assert_eq(q.back(), 30); +} + +fn test_queue_front_after_pop() { + q := Queue(); + q.push(1); + q.push(2); + q.push(3); + q.pop(); + assert_eq(q.front(), 2); +} + +// ============================================ +// Queue Query Methods +// ============================================ + +fn test_queue_length() { + q := Queue(); + q.push(1); + q.push(2); + q.push(3); + assert_eq(q.length(), 3); +} + +fn test_queue_is_empty_true() { + q := Queue(); + assert_eq(q.is_empty(), true); +} + +fn test_queue_is_empty_false() { + q := Queue(); + q.push(1); + assert_eq(q.is_empty(), false); +} + +fn test_queue_is_empty_after_pop() { + q := Queue(); + q.push(1); + q.pop(); + assert_eq(q.is_empty(), true); +} + +// ============================================ +// Typed Variable Declaration +// ============================================ + +fn test_queue_typed_decl() { + Queue q = Queue(); + q.push(1); + assert_eq(q.length(), 1); +} + +// ============================================ +// Queue with bool +// ============================================ + +fn test_queue_bool() { + q := Queue(); + q.push(true); + q.push(false); + assert_eq(q.pop(), true); + assert_eq(q.pop(), false); +} diff --git a/tests/test_stack.b b/tests/test_stack.b new file mode 100644 index 0000000..9b70516 --- /dev/null +++ b/tests/test_stack.b @@ -0,0 +1,143 @@ +// ============================================ +// Stack Creation +// ============================================ + +fn test_stack_create_empty_int() { + s := Stack(); + assert_eq(s.length(), 0); +} + +fn test_stack_create_empty_str() { + s := Stack(); + assert_eq(s.is_empty(), true); +} + +// ============================================ +// Stack Push +// ============================================ + +fn test_stack_push() { + s := Stack(); + s.push(1); + s.push(2); + s.push(3); + assert_eq(s.length(), 3); +} + +fn test_stack_push_str() { + s := Stack(); + s.push("hello"); + s.push("world"); + assert_eq(s.length(), 2); +} + +// ============================================ +// Stack Pop (LIFO) +// ============================================ + +fn test_stack_pop() { + s := Stack(); + s.push(1); + s.push(2); + s.push(3); + val := s.pop(); + assert_eq(val, 3); + assert_eq(s.length(), 2); +} + +fn test_stack_pop_multiple() { + s := Stack(); + s.push(1); + s.push(2); + s.push(3); + first := s.pop(); + second := s.pop(); + third := s.pop(); + assert_eq(first, 3); + assert_eq(second, 2); + assert_eq(third, 1); +} + +fn test_stack_lifo_order() { + s := Stack(); + s.push("a"); + s.push("b"); + s.push("c"); + assert_eq(s.pop(), "c"); + assert_eq(s.pop(), "b"); + assert_eq(s.pop(), "a"); +} + +// ============================================ +// Stack Top +// ============================================ + +fn test_stack_top() { + s := Stack(); + s.push(10); + s.push(20); + s.push(30); + assert_eq(s.top(), 30); + assert_eq(s.length(), 3); +} + +fn test_stack_top_after_pop() { + s := Stack(); + s.push(1); + s.push(2); + s.push(3); + s.pop(); + assert_eq(s.top(), 2); +} + +// ============================================ +// Stack Query Methods +// ============================================ + +fn test_stack_length() { + s := Stack(); + s.push(1); + s.push(2); + s.push(3); + assert_eq(s.length(), 3); +} + +fn test_stack_is_empty_true() { + s := Stack(); + assert_eq(s.is_empty(), true); +} + +fn test_stack_is_empty_false() { + s := Stack(); + s.push(1); + assert_eq(s.is_empty(), false); +} + +fn test_stack_is_empty_after_pop() { + s := Stack(); + s.push(1); + s.pop(); + assert_eq(s.is_empty(), true); +} + +// ============================================ +// Typed Variable Declaration +// ============================================ + +fn test_stack_typed_decl() { + Stack s = Stack(); + s.push(1); + assert_eq(s.length(), 1); +} + +// ============================================ +// Stack with bool +// ============================================ + +fn test_stack_bool() { + s := Stack(); + s.push(true); + s.push(false); + assert_eq(s.pop(), false); + assert_eq(s.pop(), true); +} diff --git a/typechecker/check_deque.cpp b/typechecker/check_deque.cpp new file mode 100644 index 0000000..479d786 --- /dev/null +++ b/typechecker/check_deque.cpp @@ -0,0 +1,61 @@ +/** + * @file check_deque.cpp + * @brief Deque type inference for the Bishop type checker. + */ + +#include "typechecker.hpp" +#include "deques.hpp" + +using namespace std; + +namespace typechecker { + +/** + * Infers the type of a deque creation expression. + */ +TypeInfo check_deque_create(TypeCheckerState& state, const DequeCreate& deque) { + (void)state; + return {"Deque<" + deque.element_type + ">", false, false}; +} + +/** + * Type checks a method call on a deque. + */ +TypeInfo check_deque_method(TypeCheckerState& state, const MethodCall& mcall, const string& element_type) { + auto method_info = bishop::get_deque_method_info(mcall.method_name); + + if (!method_info) { + error(state, "Deque has no method '" + mcall.method_name + "'", mcall.line); + return {"unknown", false, false}; + } + + const auto& [param_types, return_type] = *method_info; + + if (mcall.args.size() != param_types.size()) { + error(state, "method '" + mcall.method_name + "' expects " + + to_string(param_types.size()) + " arguments, got " + + to_string(mcall.args.size()), mcall.line); + } + + for (size_t i = 0; i < mcall.args.size() && i < param_types.size(); i++) { + TypeInfo arg_type = infer_type(state, *mcall.args[i]); + string expected = param_types[i] == "T" ? element_type : param_types[i]; + TypeInfo expected_type = {expected, false, false}; + + if (!types_compatible(expected_type, arg_type)) { + error(state, "argument " + to_string(i + 1) + " of method '" + + mcall.method_name + "' expects '" + expected + + "', got '" + format_type(arg_type) + "'", mcall.line); + } + } + + string ret = return_type == "T" ? element_type : return_type; + + if (ret == "void") { + return {"void", false, true}; + } + + return {ret, false, false}; +} + +} // namespace typechecker diff --git a/typechecker/check_expression.cpp b/typechecker/check_expression.cpp index 46c0bc7..09e168e 100644 --- a/typechecker/check_expression.cpp +++ b/typechecker/check_expression.cpp @@ -95,6 +95,18 @@ TypeInfo infer_type(TypeCheckerState& state, const ASTNode& expr) { return check_tuple_create(state, *tuple); } + if (auto* deque = dynamic_cast(&expr)) { + return check_deque_create(state, *deque); + } + + if (auto* stack = dynamic_cast(&expr)) { + return check_stack_create(state, *stack); + } + + if (auto* queue = dynamic_cast(&expr)) { + return check_queue_create(state, *queue); + } + if (auto* lambda = dynamic_cast(&expr)) { return check_lambda_expr(state, *lambda); } diff --git a/typechecker/check_method_call.cpp b/typechecker/check_method_call.cpp index 8c66e89..623d5f2 100644 --- a/typechecker/check_method_call.cpp +++ b/typechecker/check_method_call.cpp @@ -258,6 +258,39 @@ TypeInfo check_method_call(TypeCheckerState& state, const MethodCall& mcall) { return check_tuple_method(state, mcall, element_type); } + if (effective_type.base_type.rfind("Deque<", 0) == 0) { + string element_type = extract_element_type(effective_type.base_type, "Deque<"); + + if (element_type.empty()) { + error(state, "malformed Deque type '" + effective_type.base_type + "'", mcall.line); + return {"unknown", false, false}; + } + + return check_deque_method(state, mcall, element_type); + } + + if (effective_type.base_type.rfind("Stack<", 0) == 0) { + string element_type = extract_element_type(effective_type.base_type, "Stack<"); + + if (element_type.empty()) { + error(state, "malformed Stack type '" + effective_type.base_type + "'", mcall.line); + return {"unknown", false, false}; + } + + return check_stack_method(state, mcall, element_type); + } + + if (effective_type.base_type.rfind("Queue<", 0) == 0) { + string element_type = extract_element_type(effective_type.base_type, "Queue<"); + + if (element_type.empty()) { + error(state, "malformed Queue type '" + effective_type.base_type + "'", mcall.line); + return {"unknown", false, false}; + } + + return check_queue_method(state, mcall, element_type); + } + if (effective_type.base_type == "str") { return check_str_method(state, mcall); } diff --git a/typechecker/check_queue.cpp b/typechecker/check_queue.cpp new file mode 100644 index 0000000..e9c36d1 --- /dev/null +++ b/typechecker/check_queue.cpp @@ -0,0 +1,61 @@ +/** + * @file check_queue.cpp + * @brief Queue type inference for the Bishop type checker. + */ + +#include "typechecker.hpp" +#include "queues.hpp" + +using namespace std; + +namespace typechecker { + +/** + * Infers the type of a queue creation expression. + */ +TypeInfo check_queue_create(TypeCheckerState& state, const QueueCreate& queue) { + (void)state; + return {"Queue<" + queue.element_type + ">", false, false}; +} + +/** + * Type checks a method call on a queue. + */ +TypeInfo check_queue_method(TypeCheckerState& state, const MethodCall& mcall, const string& element_type) { + auto method_info = bishop::get_queue_method_info(mcall.method_name); + + if (!method_info) { + error(state, "Queue has no method '" + mcall.method_name + "'", mcall.line); + return {"unknown", false, false}; + } + + const auto& [param_types, return_type] = *method_info; + + if (mcall.args.size() != param_types.size()) { + error(state, "method '" + mcall.method_name + "' expects " + + to_string(param_types.size()) + " arguments, got " + + to_string(mcall.args.size()), mcall.line); + } + + for (size_t i = 0; i < mcall.args.size() && i < param_types.size(); i++) { + TypeInfo arg_type = infer_type(state, *mcall.args[i]); + string expected = param_types[i] == "T" ? element_type : param_types[i]; + TypeInfo expected_type = {expected, false, false}; + + if (!types_compatible(expected_type, arg_type)) { + error(state, "argument " + to_string(i + 1) + " of method '" + + mcall.method_name + "' expects '" + expected + + "', got '" + format_type(arg_type) + "'", mcall.line); + } + } + + string ret = return_type == "T" ? element_type : return_type; + + if (ret == "void") { + return {"void", false, true}; + } + + return {ret, false, false}; +} + +} // namespace typechecker diff --git a/typechecker/check_stack.cpp b/typechecker/check_stack.cpp new file mode 100644 index 0000000..ed0b9de --- /dev/null +++ b/typechecker/check_stack.cpp @@ -0,0 +1,61 @@ +/** + * @file check_stack.cpp + * @brief Stack type inference for the Bishop type checker. + */ + +#include "typechecker.hpp" +#include "stacks.hpp" + +using namespace std; + +namespace typechecker { + +/** + * Infers the type of a stack creation expression. + */ +TypeInfo check_stack_create(TypeCheckerState& state, const StackCreate& stack) { + (void)state; + return {"Stack<" + stack.element_type + ">", false, false}; +} + +/** + * Type checks a method call on a stack. + */ +TypeInfo check_stack_method(TypeCheckerState& state, const MethodCall& mcall, const string& element_type) { + auto method_info = bishop::get_stack_method_info(mcall.method_name); + + if (!method_info) { + error(state, "Stack has no method '" + mcall.method_name + "'", mcall.line); + return {"unknown", false, false}; + } + + const auto& [param_types, return_type] = *method_info; + + if (mcall.args.size() != param_types.size()) { + error(state, "method '" + mcall.method_name + "' expects " + + to_string(param_types.size()) + " arguments, got " + + to_string(mcall.args.size()), mcall.line); + } + + for (size_t i = 0; i < mcall.args.size() && i < param_types.size(); i++) { + TypeInfo arg_type = infer_type(state, *mcall.args[i]); + string expected = param_types[i] == "T" ? element_type : param_types[i]; + TypeInfo expected_type = {expected, false, false}; + + if (!types_compatible(expected_type, arg_type)) { + error(state, "argument " + to_string(i + 1) + " of method '" + + mcall.method_name + "' expects '" + expected + + "', got '" + format_type(arg_type) + "'", mcall.line); + } + } + + string ret = return_type == "T" ? element_type : return_type; + + if (ret == "void") { + return {"void", false, true}; + } + + return {ret, false, false}; +} + +} // namespace typechecker diff --git a/typechecker/deques.cpp b/typechecker/deques.cpp new file mode 100644 index 0000000..3f0c868 --- /dev/null +++ b/typechecker/deques.cpp @@ -0,0 +1,153 @@ +/** + * @file deques.cpp + * @brief Deque method type definitions for the Bishop type checker. + * + * Defines type signatures for all built-in Deque methods. + * Uses "T" as a placeholder for the element type, which is + * substituted with the actual type at type check time. + */ + +/** + * @bishop_method push_front + * @type Deque + * @description Adds an element to the front of the deque. + * @param elem T - The element to add + * @example + * dq := Deque(); + * dq.push_front(1); + */ + +/** + * @bishop_method push_back + * @type Deque + * @description Adds an element to the back of the deque. + * @param elem T - The element to add + * @example + * dq := Deque(); + * dq.push_back(1); + */ + +/** + * @bishop_method pop_front + * @type Deque + * @description Removes and returns the front element. + * @returns T - The removed element + * @example + * dq := Deque(); + * dq.push_back(1); + * val := dq.pop_front(); // 1 + */ + +/** + * @bishop_method pop_back + * @type Deque + * @description Removes and returns the back element. + * @returns T - The removed element + * @example + * dq := Deque(); + * dq.push_back(1); + * val := dq.pop_back(); // 1 + */ + +/** + * @bishop_method front + * @type Deque + * @description Returns the front element without removing it. + * @returns T - The front element + * @example + * dq := Deque(); + * dq.push_back(1); + * val := dq.front(); // 1 + */ + +/** + * @bishop_method back + * @type Deque + * @description Returns the back element without removing it. + * @returns T - The back element + * @example + * dq := Deque(); + * dq.push_back(1); + * val := dq.back(); // 1 + */ + +/** + * @bishop_method get + * @type Deque + * @description Returns the element at the specified index. + * @param index int - The index (0-based) + * @returns T - The element at that position + * @example + * dq := Deque(); + * dq.push_back(10); + * dq.push_back(20); + * val := dq.get(1); // 20 + */ + +/** + * @bishop_method length + * @type Deque + * @description Returns the number of elements in the deque. + * @returns int - The deque length + * @example + * dq := Deque(); + * dq.push_back(1); + * len := dq.length(); // 1 + */ + +/** + * @bishop_method is_empty + * @type Deque + * @description Returns true if the deque has no elements. + * @returns bool - True if empty, false otherwise + * @example + * dq := Deque(); + * if dq.is_empty() { print("Empty"); } + */ + +/** + * @bishop_method clear + * @type Deque + * @description Removes all elements from the deque. + * @example + * dq := Deque(); + * dq.push_back(1); + * dq.clear(); // dq is now empty + */ + +#include "deques.hpp" + +#include + +namespace bishop { + +std::optional get_deque_method_info(const std::string& method_name) { + // "T" is placeholder for element type, substituted at type check time + static const std::map deque_methods = { + // Modification methods + {"push_front", {{"T"}, "void"}}, + {"push_back", {{"T"}, "void"}}, + {"pop_front", {{}, "T"}}, + {"pop_back", {{}, "T"}}, + {"clear", {{}, "void"}}, + + // Access methods + {"front", {{}, "T"}}, + {"back", {{}, "T"}}, + {"get", {{"int"}, "T"}}, + + // Query methods + {"length", {{}, "int"}}, + {"is_empty", {{}, "bool"}}, + }; + + auto it = deque_methods.find(method_name); + + if (it != deque_methods.end()) { + return it->second; + } + + return std::nullopt; +} + +} // namespace bishop diff --git a/typechecker/deques.hpp b/typechecker/deques.hpp new file mode 100644 index 0000000..5510be9 --- /dev/null +++ b/typechecker/deques.hpp @@ -0,0 +1,29 @@ +/** + * @file deques.hpp + * @brief Deque method type information for the Bishop type checker. + */ + +#pragma once + +#include +#include +#include + +namespace bishop { + +/** + * Represents a deque method signature with parameter types and return type. + * Uses "T" as a placeholder for the element type. + */ +struct DequeMethodInfo { + std::vector param_types; + std::string return_type; +}; + +/** + * Returns type information for built-in Deque methods. + * Returns nullopt if the method is not found. + */ +std::optional get_deque_method_info(const std::string& method_name); + +} // namespace bishop diff --git a/typechecker/queues.cpp b/typechecker/queues.cpp new file mode 100644 index 0000000..b078c57 --- /dev/null +++ b/typechecker/queues.cpp @@ -0,0 +1,105 @@ +/** + * @file queues.cpp + * @brief Queue method type definitions for the Bishop type checker. + * + * Defines type signatures for all built-in Queue methods. + * Uses "T" as a placeholder for the element type, which is + * substituted with the actual type at type check time. + */ + +/** + * @bishop_method push + * @type Queue + * @description Adds an element to the back of the queue. + * @param elem T - The element to add + * @example + * q := Queue(); + * q.push(1); + */ + +/** + * @bishop_method pop + * @type Queue + * @description Removes and returns the front element (FIFO). + * @returns T - The removed element + * @example + * q := Queue(); + * q.push(1); + * val := q.pop(); // 1 + */ + +/** + * @bishop_method front + * @type Queue + * @description Returns the front element without removing it. + * @returns T - The front element + * @example + * q := Queue(); + * q.push(1); + * val := q.front(); // 1 + */ + +/** + * @bishop_method back + * @type Queue + * @description Returns the back element without removing it. + * @returns T - The back element + * @example + * q := Queue(); + * q.push(1); + * val := q.back(); // 1 + */ + +/** + * @bishop_method length + * @type Queue + * @description Returns the number of elements in the queue. + * @returns int - The queue size + * @example + * q := Queue(); + * q.push(1); + * len := q.length(); // 1 + */ + +/** + * @bishop_method is_empty + * @type Queue + * @description Returns true if the queue has no elements. + * @returns bool - True if empty, false otherwise + * @example + * q := Queue(); + * if q.is_empty() { print("Empty"); } + */ + +#include "queues.hpp" + +#include + +namespace bishop { + +std::optional get_queue_method_info(const std::string& method_name) { + // "T" is placeholder for element type, substituted at type check time + static const std::map queue_methods = { + // Modification methods + {"push", {{"T"}, "void"}}, + {"pop", {{}, "T"}}, + + // Access methods + {"front", {{}, "T"}}, + {"back", {{}, "T"}}, + + // Query methods + {"length", {{}, "int"}}, + {"is_empty", {{}, "bool"}}, + }; + + auto it = queue_methods.find(method_name); + + if (it != queue_methods.end()) { + return it->second; + } + + return std::nullopt; +} + +} // namespace bishop diff --git a/typechecker/queues.hpp b/typechecker/queues.hpp new file mode 100644 index 0000000..78faece --- /dev/null +++ b/typechecker/queues.hpp @@ -0,0 +1,29 @@ +/** + * @file queues.hpp + * @brief Queue method type information for the Bishop type checker. + */ + +#pragma once + +#include +#include +#include + +namespace bishop { + +/** + * Represents a queue method signature with parameter types and return type. + * Uses "T" as a placeholder for the element type. + */ +struct QueueMethodInfo { + std::vector param_types; + std::string return_type; +}; + +/** + * Returns type information for built-in Queue methods. + * Returns nullopt if the method is not found. + */ +std::optional get_queue_method_info(const std::string& method_name); + +} // namespace bishop diff --git a/typechecker/stacks.cpp b/typechecker/stacks.cpp new file mode 100644 index 0000000..1f011c9 --- /dev/null +++ b/typechecker/stacks.cpp @@ -0,0 +1,93 @@ +/** + * @file stacks.cpp + * @brief Stack method type definitions for the Bishop type checker. + * + * Defines type signatures for all built-in Stack methods. + * Uses "T" as a placeholder for the element type, which is + * substituted with the actual type at type check time. + */ + +/** + * @bishop_method push + * @type Stack + * @description Pushes an element onto the top of the stack. + * @param elem T - The element to push + * @example + * s := Stack(); + * s.push(1); + */ + +/** + * @bishop_method pop + * @type Stack + * @description Removes and returns the top element (LIFO). + * @returns T - The removed element + * @example + * s := Stack(); + * s.push(1); + * val := s.pop(); // 1 + */ + +/** + * @bishop_method top + * @type Stack + * @description Returns the top element without removing it. + * @returns T - The top element + * @example + * s := Stack(); + * s.push(1); + * val := s.top(); // 1 + */ + +/** + * @bishop_method length + * @type Stack + * @description Returns the number of elements in the stack. + * @returns int - The stack size + * @example + * s := Stack(); + * s.push(1); + * len := s.length(); // 1 + */ + +/** + * @bishop_method is_empty + * @type Stack + * @description Returns true if the stack has no elements. + * @returns bool - True if empty, false otherwise + * @example + * s := Stack(); + * if s.is_empty() { print("Empty"); } + */ + +#include "stacks.hpp" + +#include + +namespace bishop { + +std::optional get_stack_method_info(const std::string& method_name) { + // "T" is placeholder for element type, substituted at type check time + static const std::map stack_methods = { + // Modification methods + {"push", {{"T"}, "void"}}, + {"pop", {{}, "T"}}, + + // Access methods + {"top", {{}, "T"}}, + + // Query methods + {"length", {{}, "int"}}, + {"is_empty", {{}, "bool"}}, + }; + + auto it = stack_methods.find(method_name); + + if (it != stack_methods.end()) { + return it->second; + } + + return std::nullopt; +} + +} // namespace bishop diff --git a/typechecker/stacks.hpp b/typechecker/stacks.hpp new file mode 100644 index 0000000..01bcc2c --- /dev/null +++ b/typechecker/stacks.hpp @@ -0,0 +1,29 @@ +/** + * @file stacks.hpp + * @brief Stack method type information for the Bishop type checker. + */ + +#pragma once + +#include +#include +#include + +namespace bishop { + +/** + * Represents a stack method signature with parameter types and return type. + * Uses "T" as a placeholder for the element type. + */ +struct StackMethodInfo { + std::vector param_types; + std::string return_type; +}; + +/** + * Returns type information for built-in Stack methods. + * Returns nullopt if the method is not found. + */ +std::optional get_stack_method_info(const std::string& method_name); + +} // namespace bishop diff --git a/typechecker/typechecker.hpp b/typechecker/typechecker.hpp index 02201e2..2941bb2 100644 --- a/typechecker/typechecker.hpp +++ b/typechecker/typechecker.hpp @@ -191,6 +191,18 @@ TypeInfo check_pair_field(TypeCheckerState& state, const FieldAccess& access, co TypeInfo check_tuple_create(TypeCheckerState& state, const TupleCreate& tuple); TypeInfo check_tuple_method(TypeCheckerState& state, const MethodCall& mcall, const std::string& element_type); +// Deque type inference (check_deque.cpp) +TypeInfo check_deque_create(TypeCheckerState& state, const DequeCreate& deque); +TypeInfo check_deque_method(TypeCheckerState& state, const MethodCall& mcall, const std::string& element_type); + +// Stack type inference (check_stack.cpp) +TypeInfo check_stack_create(TypeCheckerState& state, const StackCreate& stack); +TypeInfo check_stack_method(TypeCheckerState& state, const MethodCall& mcall, const std::string& element_type); + +// Queue type inference (check_queue.cpp) +TypeInfo check_queue_create(TypeCheckerState& state, const QueueCreate& queue); +TypeInfo check_queue_method(TypeCheckerState& state, const MethodCall& mcall, const std::string& element_type); + // Function call type inference (check_function_call.cpp) TypeInfo check_function_call(TypeCheckerState& state, const FunctionCall& call); From 508f15f0bfe2e6e7f45d4ea8113cf6e53b61da59 Mon Sep 17 00:00:00 2001 From: "clank-hayen[bot]" Date: Fri, 2 Jan 2026 03:02:17 +0000 Subject: [PATCH 2/4] fix: add typed variable declaration parsing for Deque, Stack, Queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parser was missing support for typed variable declarations using Deque, Stack, and Queue types. This caused parse errors like 'unexpected token >' when using syntax like: Queue q = Queue(); Added handling in parse_statement.cpp for these collection types to match existing patterns for List, Pair, Tuple, and Channel. Also added type parsing in parse_type.cpp to recognize Deque, Stack, and Queue as valid type tokens for function parameters and return types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- parser/parse_statement.cpp | 63 ++++++++++++++++++++++++++++++++++++++ parser/parse_type.cpp | 27 ++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/parser/parse_statement.cpp b/parser/parse_statement.cpp index 1fd9b1f..03a2f5a 100644 --- a/parser/parse_statement.cpp +++ b/parser/parse_statement.cpp @@ -231,6 +231,69 @@ unique_ptr parse_statement(ParserState& state) { return decl; } + // Deque variable declaration: Deque dq = Deque(); or Deque> x = ...; + if (check(state, TokenType::DEQUE)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Deque> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + + auto decl = make_unique(); + decl->type = "Deque<" + element_type + ">"; + decl->line = start_line; + decl->name = consume(state, TokenType::IDENT).value; + consume(state, TokenType::ASSIGN); + decl->value = parse_expression(state); + consume(state, TokenType::SEMICOLON); + return decl; + } + + // Stack variable declaration: Stack s = Stack(); or Stack> x = ...; + if (check(state, TokenType::STACK)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Stack> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + + auto decl = make_unique(); + decl->type = "Stack<" + element_type + ">"; + decl->line = start_line; + decl->name = consume(state, TokenType::IDENT).value; + consume(state, TokenType::ASSIGN); + decl->value = parse_expression(state); + consume(state, TokenType::SEMICOLON); + return decl; + } + + // Queue variable declaration: Queue q = Queue(); or Queue> x = ...; + if (check(state, TokenType::QUEUE)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like Queue> + string element_type = parse_type(state); + + consume(state, TokenType::GT); + + auto decl = make_unique(); + decl->type = "Queue<" + element_type + ">"; + decl->line = start_line; + decl->name = consume(state, TokenType::IDENT).value; + consume(state, TokenType::ASSIGN); + decl->value = parse_expression(state); + consume(state, TokenType::SEMICOLON); + return decl; + } + // NOT expression statement: !expr or fail "msg"; !valid or continue; etc. if (check(state, TokenType::NOT)) { auto expr = parse_expression(state); diff --git a/parser/parse_type.cpp b/parser/parse_type.cpp index d86fbb0..b966bff 100644 --- a/parser/parse_type.cpp +++ b/parser/parse_type.cpp @@ -156,6 +156,33 @@ string parse_base_type(ParserState& state) { return "Tuple<" + element_type + ">"; } + // Deque type + if (check(state, TokenType::DEQUE)) { + advance(state); + consume(state, TokenType::LT); + string element_type = parse_type(state); + consume(state, TokenType::GT); + return "Deque<" + element_type + ">"; + } + + // Stack type + if (check(state, TokenType::STACK)) { + advance(state); + consume(state, TokenType::LT); + string element_type = parse_type(state); + consume(state, TokenType::GT); + return "Stack<" + element_type + ">"; + } + + // Queue type + if (check(state, TokenType::QUEUE)) { + advance(state); + consume(state, TokenType::LT); + string element_type = parse_type(state); + consume(state, TokenType::GT); + return "Queue<" + element_type + ">"; + } + // Custom type (struct name), qualified type (module.Type), or generic (Type) if (check(state, TokenType::IDENT)) { string type = current(state).value; From 20b36476ad43c99e78fe1174a33cd1bf31fcaebf Mon Sep 17 00:00:00 2001 From: "clank-hayen[bot]" Date: Fri, 2 Jan 2026 03:07:57 +0000 Subject: [PATCH 3/4] fix: add type validation for Deque, Stack, Queue collection types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The is_valid_type function in typechecker.cpp was missing validation for Deque, Stack, and Queue types. This caused 'unknown type' errors when using typed variable declarations like: Queue q = Queue(); Added validation patterns for these collection types, matching the existing patterns for List, Pair, Tuple, and Channel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- typechecker/typechecker.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/typechecker/typechecker.cpp b/typechecker/typechecker.cpp index 4a63d0a..26e6a4f 100644 --- a/typechecker/typechecker.cpp +++ b/typechecker/typechecker.cpp @@ -267,6 +267,36 @@ bool is_valid_type(const TypeCheckerState& state, const string& type) { return is_valid_type(state, element_type); } + if (type.rfind("Deque<", 0) == 0 && type.back() == '>') { + string element_type = extract_element_type(type, "Deque<"); + + if (element_type.empty()) { + return false; + } + + return is_valid_type(state, element_type); + } + + if (type.rfind("Stack<", 0) == 0 && type.back() == '>') { + string element_type = extract_element_type(type, "Stack<"); + + if (element_type.empty()) { + return false; + } + + return is_valid_type(state, element_type); + } + + if (type.rfind("Queue<", 0) == 0 && type.back() == '>') { + string element_type = extract_element_type(type, "Queue<"); + + if (element_type.empty()) { + return false; + } + + return is_valid_type(state, element_type); + } + // Pointer type: StructName* -> check that base is a valid struct if (!type.empty() && type.back() == '*') { string pointee = type.substr(0, type.length() - 1); From 76b3c2349fe4390355e47ef2ae32d8b6a133c479 Mon Sep 17 00:00:00 2001 From: "clank-hayen[bot]" Date: Fri, 2 Jan 2026 03:11:59 +0000 Subject: [PATCH 4/4] fix: add deque, stack, queue includes to runtime std.hpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The C++ standard library headers for , , and were not included in the Bishop runtime, causing compilation errors when using the new Deque, Stack, and Queue collection types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- runtime/std/std.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/std/std.hpp b/runtime/std/std.hpp index 9d5c96b..a45dee5 100644 --- a/runtime/std/std.hpp +++ b/runtime/std/std.hpp @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include // Error handling primitives #include