diff --git a/CMakeLists.txt b/CMakeLists.txt index 15be90a..7460268 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,8 @@ add_library(bishop_lib typechecker/sets.cpp typechecker/check_pair.cpp typechecker/check_tuple.cpp + typechecker/check_priority_queue.cpp + typechecker/priority_queues.cpp typechecker/check_map.cpp typechecker/check_set.cpp typechecker/check_lambda.cpp @@ -129,6 +131,7 @@ add_library(bishop_lib codegen/emit_string.cpp codegen/emit_pair.cpp codegen/emit_tuple.cpp + codegen/emit_priority_queue.cpp codegen/emit_set.cpp codegen/emit_method_call.cpp codegen/emit_function_call.cpp @@ -173,6 +176,9 @@ add_custom_target(bishop_runtime_headers ALL COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/runtime/std/channel.hpp ${CMAKE_BINARY_DIR}/include/bishop/channel.hpp + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/runtime/collections/priority_queue.hpp + ${CMAKE_BINARY_DIR}/include/bishop/priority_queue.hpp COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/runtime/http/http.hpp ${CMAKE_BINARY_DIR}/include/bishop/http.hpp diff --git a/README.md b/README.md index e2d9c14..5c78981 100644 --- a/README.md +++ b/README.md @@ -749,6 +749,82 @@ for i in 0..5 { // sum is 15 ``` +## PriorityQueue + +Priority queues maintain elements in priority order. By default, they are max heaps (highest value first). + +### Max Heap (Default) + +```bishop +pq := PriorityQueue(); // max heap by default + +pq.push(5); +pq.push(10); +pq.push(3); + +pq.top(); // 10 (peek highest priority) +val := pq.pop(); // 10 (remove and return highest) +pq.pop(); // 5 +pq.pop(); // 3 +``` + +### Min Heap + +Use `.min()` to create a min heap (lowest value first): + +```bishop +pq := PriorityQueue.min(); + +pq.push(5); +pq.push(10); +pq.push(3); + +pq.top(); // 3 (peek lowest priority) +val := pq.pop(); // 3 (remove and return lowest) +pq.pop(); // 5 +pq.pop(); // 10 +``` + +### Custom Comparison + +For custom structs, define a `less_than` method to control priority ordering: + +```bishop +Task :: struct { + name str, + priority int +} + +Task :: less_than(self, Task other) -> bool { + return self.priority < other.priority; +} + +// Max heap: highest priority value first +pq := PriorityQueue(); +pq.push(Task { name: "low", priority: 10 }); +pq.push(Task { name: "high", priority: 1 }); +pq.push(Task { name: "med", priority: 5 }); + +task := pq.pop(); // Task with priority 10 (highest) + +// Min heap: lowest priority value first +pq_min := PriorityQueue.min(); +pq_min.push(Task { name: "low", priority: 10 }); +pq_min.push(Task { name: "high", priority: 1 }); + +task := pq_min.pop(); // Task with priority 1 (lowest) +``` + +### PriorityQueue Methods + +| Method | Returns | Description | +|--------|---------|-------------| +| `push(elem)` | `void` | Add element to queue | +| `pop()` | `T` | Remove and return highest priority element | +| `top()` | `T` | Peek at highest priority element without removing | +| `length()` | `int` | Number of elements in queue | +| `is_empty()` | `bool` | True if queue has no elements | + ## Sets Sets store unique elements with O(1) lookup. Duplicate elements are automatically deduplicated. @@ -3014,7 +3090,7 @@ fn test_math() { ## Keywords -`fn`, `return`, `struct`, `if`, `else`, `while`, `for`, `in`, `true`, `false`, `none`, `is`, `import`, `using`, `select`, `case`, `Channel`, `List`, `Pair`, `Tuple`, `extern`, `go`, `sleep`, `err`, `fail`, `or`, `match`, `default`, `with`, `as`, `const`, `continue`, `break` +`fn`, `return`, `struct`, `if`, `else`, `while`, `for`, `in`, `true`, `false`, `none`, `is`, `import`, `using`, `select`, `case`, `Channel`, `List`, `Pair`, `Tuple`, `PriorityQueue`, `extern`, `go`, `sleep`, `err`, `fail`, `or`, `match`, `default`, `with`, `as`, `const`, `continue`, `break` ## Decorators diff --git a/codegen/codegen.hpp b/codegen/codegen.hpp index cdf9efa..fbf4366 100644 --- a/codegen/codegen.hpp +++ b/codegen/codegen.hpp @@ -118,6 +118,10 @@ 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); +// PriorityQueue (emit_priority_queue.cpp) +std::string emit_priority_queue_create(CodeGenState& state, const PriorityQueueCreate& pq); +std::string emit_priority_queue_method_call(CodeGenState& state, const MethodCall& call, const std::string& obj_str, const std::vector& args); + // Set (emit_set.cpp) std::string emit_set_create(const SetCreate& set); std::string emit_set_literal(CodeGenState& state, const SetLiteral& set); diff --git a/codegen/emit_expression.cpp b/codegen/emit_expression.cpp index 5153e2b..666d656 100644 --- a/codegen/emit_expression.cpp +++ b/codegen/emit_expression.cpp @@ -114,6 +114,10 @@ string emit(CodeGenState& state, const ASTNode& node) { return emit_tuple_create(state, *tuple); } + if (auto* pq = dynamic_cast(&node)) { + return emit_priority_queue_create(state, *pq); + } + if (auto* set = dynamic_cast(&node)) { return emit_set_create(*set); } diff --git a/codegen/emit_method_call.cpp b/codegen/emit_method_call.cpp index 363b882..38d43e2 100644 --- a/codegen/emit_method_call.cpp +++ b/codegen/emit_method_call.cpp @@ -118,6 +118,11 @@ string emit_method_call(CodeGenState& state, const MethodCall& call) { return emit_tuple_method_call(state, call, obj_str, args); } + // Handle PriorityQueue methods + if (call.object_type.rfind("PriorityQueue<", 0) == 0) { + return emit_priority_queue_method_call(state, call, obj_str, args); + } + // Handle Set methods if (call.object_type.rfind("Set<", 0) == 0) { return emit_set_method_call(state, call, obj_str, args); diff --git a/codegen/emit_priority_queue.cpp b/codegen/emit_priority_queue.cpp new file mode 100644 index 0000000..409b6db --- /dev/null +++ b/codegen/emit_priority_queue.cpp @@ -0,0 +1,59 @@ +/** + * @file emit_priority_queue.cpp + * @brief PriorityQueue emission for the Bishop code generator. + */ + +#include "codegen.hpp" +#include + +using namespace std; + +namespace codegen { + +/** + * Emits a priority queue creation. + * Max heap: PriorityQueue() -> bishop::MaxPriorityQueue() + * Min heap: PriorityQueue.min() -> bishop::MinPriorityQueue() + */ +string emit_priority_queue_create(CodeGenState& state, const PriorityQueueCreate& pq) { + (void)state; // Unused + string cpp_type = map_type(pq.element_type); + + if (pq.is_min_heap) { + return "bishop::MinPriorityQueue<" + cpp_type + ">()"; + } else { + return "bishop::MaxPriorityQueue<" + cpp_type + ">()"; + } +} + +/** + * Emits a priority queue method call. + */ +string emit_priority_queue_method_call(CodeGenState& state, const MethodCall& call, const string& obj_str, const vector& args) { + (void)state; // Unused + + 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 obj_str + ".pop()"; + } + + if (call.method_name == "top") { + return obj_str + ".top()"; + } + + // Unknown 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 bab0805..2acfc13 100644 --- a/codegen/emit_type.cpp +++ b/codegen/emit_type.cpp @@ -123,6 +123,14 @@ string map_type(const string& t) { return "std::vector<" + map_type(element_type) + ">"; } + // Handle PriorityQueue types: PriorityQueue -> bishop::MaxPriorityQueue + // Note: This maps to MaxPriorityQueue by default; min heap is handled at creation time + if (t.rfind("PriorityQueue<", 0) == 0 && t.back() == '>') { + string element_type = extract_element_type(t, "PriorityQueue<"); + assert(!element_type.empty() && "malformed PriorityQueue type passed typechecker"); + return "bishop::PriorityQueueBase<" + map_type(element_type) + ">"; + } + // Handle Set types: Set -> std::unordered_set if (t.rfind("Set<", 0) == 0 && t.back() == '>') { string element_type = extract_element_type(t, "Set<"); diff --git a/lexer/lexer.cpp b/lexer/lexer.cpp index 2a9502b..cd31585 100644 --- a/lexer/lexer.cpp +++ b/lexer/lexer.cpp @@ -37,6 +37,7 @@ static unordered_map keywords = { {"Map", TokenType::MAP}, {"Pair", TokenType::PAIR}, {"Tuple", TokenType::TUPLE}, + {"PriorityQueue", TokenType::PRIORITY_QUEUE}, {"Set", TokenType::SET}, {"select", TokenType::SELECT}, {"case", TokenType::CASE}, diff --git a/lexer/token.hpp b/lexer/token.hpp index 3ef12c9..969042b 100644 --- a/lexer/token.hpp +++ b/lexer/token.hpp @@ -162,6 +162,7 @@ enum class TokenType { MAP, PAIR, TUPLE, + PRIORITY_QUEUE, SET, SELECT, CASE, diff --git a/parser/ast.hpp b/parser/ast.hpp index 9b0d348..0abc28d 100644 --- a/parser/ast.hpp +++ b/parser/ast.hpp @@ -214,6 +214,12 @@ struct TupleCreate : ASTNode { vector> elements; ///< Element expressions (2-5 elements) }; +/** @brief Priority queue creation: PriorityQueue() or PriorityQueue.min() */ +struct PriorityQueueCreate : ASTNode { + string element_type; ///< Type of elements (e.g., "int", "Task") + bool is_min_heap; ///< True for min heap (via .min()), false for max heap (default) +}; + /** @brief Set creation: Set() */ struct SetCreate : ASTNode { string element_type; ///< Type of elements the set holds diff --git a/parser/parse_primary.cpp b/parser/parse_primary.cpp index bf1af1c..dbc7dd2 100644 --- a/parser/parse_primary.cpp +++ b/parser/parse_primary.cpp @@ -244,6 +244,43 @@ unique_ptr parse_primary(ParserState& state) { return tuple; } + // Handle priority queue creation: PriorityQueue() or PriorityQueue.min() + if (check(state, TokenType::PRIORITY_QUEUE)) { + int start_line = current(state).line; + advance(state); + consume(state, TokenType::LT); + + // Use parse_type to support nested generics like PriorityQueue + string element_type = parse_type(state); + + consume(state, TokenType::GT); + + auto pq = make_unique(); + pq->element_type = element_type; + pq->line = start_line; + pq->is_min_heap = false; // Default to max heap + + // Check for .min() static method call + if (check(state, TokenType::DOT)) { + advance(state); + Token method = consume(state, TokenType::IDENT); + + if (method.value == "min") { + consume(state, TokenType::LPAREN); + consume(state, TokenType::RPAREN); + pq->is_min_heap = true; + } else { + throw runtime_error("PriorityQueue only supports .min() constructor, got '." + method.value + "' at line " + to_string(start_line)); + } + } else { + // Default max heap: PriorityQueue() + consume(state, TokenType::LPAREN); + consume(state, TokenType::RPAREN); + } + + return pq; + } + // Handle set creation: Set() or Set() if (check(state, TokenType::SET)) { int start_line = current(state).line; diff --git a/parser/parse_type.cpp b/parser/parse_type.cpp index 3b83f52..afce5d5 100644 --- a/parser/parse_type.cpp +++ b/parser/parse_type.cpp @@ -167,6 +167,15 @@ string parse_base_type(ParserState& state) { return "Tuple<" + element_type + ">"; } + // PriorityQueue type + if (check(state, TokenType::PRIORITY_QUEUE)) { + advance(state); + consume(state, TokenType::LT); + string element_type = parse_type(state); + consume(state, TokenType::GT); + return "PriorityQueue<" + element_type + ">"; + } + // Set type if (check(state, TokenType::SET)) { advance(state); diff --git a/runtime/collections/priority_queue.hpp b/runtime/collections/priority_queue.hpp new file mode 100644 index 0000000..8d76586 --- /dev/null +++ b/runtime/collections/priority_queue.hpp @@ -0,0 +1,168 @@ +/** + * @file priority_queue.hpp + * @brief Priority queue implementation for Bishop. + * + * Provides MaxPriorityQueue and MinPriorityQueue templates that wrap + * std::priority_queue with a unified interface for both primitives + * and custom struct types with less_than methods. + */ + +#ifndef BISHOP_COLLECTIONS_PRIORITY_QUEUE_HPP +#define BISHOP_COLLECTIONS_PRIORITY_QUEUE_HPP + +#include +#include +#include +#include + +namespace bishop { + +namespace detail { + +/** + * SFINAE helper to detect if a type has a less_than method. + */ +template +struct has_less_than : std::false_type {}; + +template +struct has_less_than().less_than(std::declval()))>> : std::true_type {}; + +template +inline constexpr bool has_less_than_v = has_less_than::value; + +} // namespace detail + +/** + * Base class for priority queues providing the common interface. + * The Comparator template parameter determines max heap vs min heap behavior. + */ +template +class PriorityQueueImpl { +public: + PriorityQueueImpl() = default; + + /** + * Adds an element to the priority queue. + */ + void push(const T& value) { + pq_.push(value); + } + + /** + * Removes and returns the highest priority element. + */ + T pop() { + T top = pq_.top(); + pq_.pop(); + return top; + } + + /** + * Returns the highest priority element without removing it. + */ + const T& top() const { + return pq_.top(); + } + + /** + * Returns the number of elements in the queue. + */ + int size() const { + return static_cast(pq_.size()); + } + + /** + * Returns true if the queue is empty. + */ + bool empty() const { + return pq_.empty(); + } + +private: + std::priority_queue, Comparator> pq_; +}; + +/** + * Max heap comparator for primitive types (uses <). + */ +template +struct MaxHeapComparator { + bool operator()(const T& a, const T& b) const { + // For max heap in std::priority_queue, return true if a < b + // (this causes larger elements to be at the top) + return a < b; + } +}; + +/** + * Max heap comparator for custom types with less_than method. + * Uses const_cast because Bishop methods don't have const qualifiers yet, + * but less_than is logically const (pure comparison, no mutation). + */ +template +struct MaxHeapComparatorLessThan { + bool operator()(const T& a, const T& b) const { + // For max heap, return true if a < b + return const_cast(a).less_than(const_cast(b)); + } +}; + +/** + * Min heap comparator for primitive types (uses >). + */ +template +struct MinHeapComparator { + bool operator()(const T& a, const T& b) const { + // For min heap in std::priority_queue, return true if a > b + // (this causes smaller elements to be at the top) + return a > b; + } +}; + +/** + * Min heap comparator for custom types with less_than method. + * Uses const_cast because Bishop methods don't have const qualifiers yet, + * but less_than is logically const (pure comparison, no mutation). + */ +template +struct MinHeapComparatorLessThan { + bool operator()(const T& a, const T& b) const { + // For min heap, return true if a > b (using less_than: b < a) + return const_cast(b).less_than(const_cast(a)); + } +}; + +/** + * Max priority queue (default). + * Elements with higher priority (greater value) come first. + * For custom types, uses less_than method; for primitives uses <. + */ +template +using MaxPriorityQueue = PriorityQueueImpl, + MaxHeapComparatorLessThan, + MaxHeapComparator>>; + +/** + * Min priority queue. + * Elements with lower priority (smaller value) come first. + * For custom types, uses less_than method; for primitives uses >. + */ +template +using MinPriorityQueue = PriorityQueueImpl, + MinHeapComparatorLessThan, + MinHeapComparator>>; + +/** + * Base type alias for PriorityQueue. + * Used by codegen for type declarations. Actual instantiation + * uses MaxPriorityQueue or MinPriorityQueue directly. + */ +template +using PriorityQueueBase = MaxPriorityQueue; + +} // namespace bishop + +#endif // BISHOP_COLLECTIONS_PRIORITY_QUEUE_HPP diff --git a/runtime/std/std.hpp b/runtime/std/std.hpp index 73c6da9..61ad229 100644 --- a/runtime/std/std.hpp +++ b/runtime/std/std.hpp @@ -30,6 +30,9 @@ // Error handling primitives #include +// Collections +#include + namespace bishop::rt { // ============================================================================ diff --git a/tests/test_priority_queue.b b/tests/test_priority_queue.b new file mode 100644 index 0000000..315a62a --- /dev/null +++ b/tests/test_priority_queue.b @@ -0,0 +1,154 @@ +// Test PriorityQueue collection + +// Test max heap with integers (default) +fn test_max_heap_int() { + pq := PriorityQueue(); + + pq.push(5); + pq.push(10); + pq.push(3); + pq.push(8); + + assert_eq(4, pq.length()); + assert_eq(10, pq.top()); // Max should be at top + + val := pq.pop(); + assert_eq(10, val); + + val = pq.pop(); + assert_eq(8, val); + + val = pq.pop(); + assert_eq(5, val); + + val = pq.pop(); + assert_eq(3, val); + + assert_true(pq.is_empty()); +} + +// Test min heap with integers +fn test_min_heap_int() { + pq := PriorityQueue.min(); + + pq.push(5); + pq.push(10); + pq.push(3); + pq.push(8); + + assert_eq(4, pq.length()); + assert_eq(3, pq.top()); // Min should be at top + + val := pq.pop(); + assert_eq(3, val); + + val = pq.pop(); + assert_eq(5, val); + + val = pq.pop(); + assert_eq(8, val); + + val = pq.pop(); + assert_eq(10, val); + + assert_true(pq.is_empty()); +} + +// Test length and is_empty +fn test_length_empty() { + pq := PriorityQueue(); + + assert_true(pq.is_empty()); + assert_eq(0, pq.length()); + + pq.push("hello"); + assert_false(pq.is_empty()); + assert_eq(1, pq.length()); + + pq.push("world"); + assert_eq(2, pq.length()); + + pq.pop(); + assert_eq(1, pq.length()); + + pq.pop(); + assert_true(pq.is_empty()); +} + +// Struct with less_than method for custom comparison +Task :: struct { + name str, + priority int +} + +// Lower priority value = higher priority (like nice values) +Task :: less_than(self, Task other) -> bool { + return self.priority < other.priority; +} + +// Test max heap with custom struct using less_than +fn test_max_heap_custom() { + pq := PriorityQueue(); + + pq.push(Task { name: "low", priority: 10 }); + pq.push(Task { name: "high", priority: 1 }); + pq.push(Task { name: "medium", priority: 5 }); + + // Max heap: highest priority value first (10) + task := pq.pop(); + assert_eq("low", task.name); + + task = pq.pop(); + assert_eq("medium", task.name); + + task = pq.pop(); + assert_eq("high", task.name); +} + +// Test min heap with custom struct using less_than +fn test_min_heap_custom() { + pq := PriorityQueue.min(); + + pq.push(Task { name: "low", priority: 10 }); + pq.push(Task { name: "high", priority: 1 }); + pq.push(Task { name: "medium", priority: 5 }); + + // Min heap: lowest priority value first (1) + task := pq.pop(); + assert_eq("high", task.name); + + task = pq.pop(); + assert_eq("medium", task.name); + + task = pq.pop(); + assert_eq("low", task.name); +} + +// Test with float values +fn test_float_heap() { + max_pq := PriorityQueue(); + max_pq.push(3.14); + max_pq.push(2.71); + max_pq.push(1.41); + + assert_eq(3.14, max_pq.top()); + + min_pq := PriorityQueue.min(); + min_pq.push(3.14); + min_pq.push(2.71); + min_pq.push(1.41); + + assert_eq(1.41, min_pq.top()); +} + +// Test top does not remove element +fn test_top_no_remove() { + pq := PriorityQueue(); + pq.push(42); + + assert_eq(42, pq.top()); + assert_eq(1, pq.length()); // Still has the element + + assert_eq(42, pq.top()); + assert_eq(1, pq.length()); // Still has the element +} diff --git a/typechecker/check_expression.cpp b/typechecker/check_expression.cpp index 2f13b6b..dbd7cd4 100644 --- a/typechecker/check_expression.cpp +++ b/typechecker/check_expression.cpp @@ -103,6 +103,10 @@ TypeInfo infer_type(TypeCheckerState& state, const ASTNode& expr) { return check_tuple_create(state, *tuple); } + if (auto* pq = dynamic_cast(&expr)) { + return check_priority_queue_create(state, *pq); + } + if (auto* set = dynamic_cast(&expr)) { return check_set_create(state, *set); } diff --git a/typechecker/check_method_call.cpp b/typechecker/check_method_call.cpp index 8c7a5ed..a015dc7 100644 --- a/typechecker/check_method_call.cpp +++ b/typechecker/check_method_call.cpp @@ -270,6 +270,17 @@ TypeInfo check_method_call(TypeCheckerState& state, const MethodCall& mcall) { return check_tuple_method(state, mcall, element_type); } + if (effective_type.base_type.rfind("PriorityQueue<", 0) == 0) { + string element_type = extract_element_type(effective_type.base_type, "PriorityQueue<"); + + if (element_type.empty()) { + error(state, "malformed PriorityQueue type '" + effective_type.base_type + "'", mcall.line); + return {"unknown", false, false}; + } + + return check_priority_queue_method(state, mcall, element_type); + } + if (effective_type.base_type.rfind("Set<", 0) == 0) { string element_type = extract_element_type(effective_type.base_type, "Set<"); diff --git a/typechecker/check_priority_queue.cpp b/typechecker/check_priority_queue.cpp new file mode 100644 index 0000000..376c9f0 --- /dev/null +++ b/typechecker/check_priority_queue.cpp @@ -0,0 +1,67 @@ +/** + * @file check_priority_queue.cpp + * @brief PriorityQueue type inference for the Bishop type checker. + */ + +#include "typechecker.hpp" +#include "priority_queues.hpp" + +using namespace std; + +namespace typechecker { + +/** + * Infers the type of a priority queue creation expression. + * The is_min_heap flag is stored in the AST but doesn't affect the type. + */ +TypeInfo check_priority_queue_create(TypeCheckerState& state, const PriorityQueueCreate& pq) { + // Validate that the element type is valid + if (!is_valid_type(state, pq.element_type)) { + error(state, "unknown type '" + pq.element_type + "' in PriorityQueue", pq.line); + return {"unknown", false, false}; + } + + return {"PriorityQueue<" + pq.element_type + ">", false, false}; +} + +/** + * Type checks a method call on a priority queue. + */ +TypeInfo check_priority_queue_method(TypeCheckerState& state, const MethodCall& mcall, const string& element_type) { + auto method_info = bishop::get_priority_queue_method_info(mcall.method_name); + + if (!method_info) { + error(state, "PriorityQueue 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/priority_queues.cpp b/typechecker/priority_queues.cpp new file mode 100644 index 0000000..2aa7cdf --- /dev/null +++ b/typechecker/priority_queues.cpp @@ -0,0 +1,104 @@ +/** + * @file priority_queues.cpp + * @brief PriorityQueue method type definitions for the Bishop type checker. + * + * Defines type signatures for all built-in PriorityQueue 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 PriorityQueue + * @description Adds an element to the priority queue. + * @param elem T - The element to add + * @example + * pq := PriorityQueue(); + * pq.push(10); + * pq.push(5); + * pq.push(20); // queue now has 3 elements + */ + +/** + * @bishop_method pop + * @type PriorityQueue + * @description Removes and returns the highest priority element. + * For max heap (default), returns largest element. + * For min heap (.min()), returns smallest element. + * @returns T - The highest priority element + * @example + * pq := PriorityQueue(); + * pq.push(10); + * pq.push(5); + * pq.push(20); + * val := pq.pop(); // 20 (max heap) + */ + +/** + * @bishop_method top + * @type PriorityQueue + * @description Returns the highest priority element without removing it. + * @returns T - The highest priority element + * @example + * pq := PriorityQueue(); + * pq.push(10); + * pq.push(20); + * val := pq.top(); // 20 + * // pq still has 2 elements + */ + +/** + * @bishop_method length + * @type PriorityQueue + * @description Returns the number of elements in the priority queue. + * @returns int - The queue length + * @example + * pq := PriorityQueue(); + * pq.push(10); + * pq.push(20); + * len := pq.length(); // 2 + */ + +/** + * @bishop_method is_empty + * @type PriorityQueue + * @description Returns true if the priority queue has no elements. + * @returns bool - True if empty, false otherwise + * @example + * pq := PriorityQueue(); + * if pq.is_empty() { + * print("Queue is empty"); + * } + */ + +#include "priority_queues.hpp" + +#include + +namespace bishop { + +std::optional get_priority_queue_method_info(const std::string& method_name) { + // "T" is placeholder for element type, substituted at type check time + static const std::map pq_methods = { + // Query methods + {"length", {{}, "int"}}, + {"is_empty", {{}, "bool"}}, + + // Access methods + {"top", {{}, "T"}}, + + // Modification methods + {"push", {{"T"}, "void"}}, + {"pop", {{}, "T"}}, + }; + + auto it = pq_methods.find(method_name); + + if (it != pq_methods.end()) { + return it->second; + } + + return std::nullopt; +} + +} // namespace bishop diff --git a/typechecker/priority_queues.hpp b/typechecker/priority_queues.hpp new file mode 100644 index 0000000..b6b09e6 --- /dev/null +++ b/typechecker/priority_queues.hpp @@ -0,0 +1,29 @@ +/** + * @file priority_queues.hpp + * @brief PriorityQueue method type information for the Bishop type checker. + */ + +#pragma once + +#include +#include +#include + +namespace bishop { + +/** + * Represents a priority queue method signature with parameter types and return type. + * Uses "T" as a placeholder for the element type. + */ +struct PriorityQueueMethodInfo { + std::vector param_types; + std::string return_type; +}; + +/** + * Returns type information for built-in PriorityQueue methods. + * Returns nullopt if the method is not found. + */ +std::optional get_priority_queue_method_info(const std::string& method_name); + +} // namespace bishop diff --git a/typechecker/typechecker.hpp b/typechecker/typechecker.hpp index 946a031..fb7b480 100644 --- a/typechecker/typechecker.hpp +++ b/typechecker/typechecker.hpp @@ -197,6 +197,10 @@ 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); +// PriorityQueue type inference (check_priority_queue.cpp) +TypeInfo check_priority_queue_create(TypeCheckerState& state, const PriorityQueueCreate& pq); +TypeInfo check_priority_queue_method(TypeCheckerState& state, const MethodCall& mcall, const std::string& element_type); + // Set type inference (check_set.cpp) TypeInfo check_set_create(TypeCheckerState& state, const SetCreate& set); TypeInfo check_set_literal(TypeCheckerState& state, const SetLiteral& set);