Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(); // 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<int>.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<Task>();
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<Task>.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.
Expand Down Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions codegen/codegen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& 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<std::string>& args);

// Set (emit_set.cpp)
std::string emit_set_create(const SetCreate& set);
std::string emit_set_literal(CodeGenState& state, const SetLiteral& set);
Expand Down
4 changes: 4 additions & 0 deletions codegen/emit_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ string emit(CodeGenState& state, const ASTNode& node) {
return emit_tuple_create(state, *tuple);
}

if (auto* pq = dynamic_cast<const PriorityQueueCreate*>(&node)) {
return emit_priority_queue_create(state, *pq);
}

if (auto* set = dynamic_cast<const SetCreate*>(&node)) {
return emit_set_create(*set);
}
Expand Down
5 changes: 5 additions & 0 deletions codegen/emit_method_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
59 changes: 59 additions & 0 deletions codegen/emit_priority_queue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @file emit_priority_queue.cpp
* @brief PriorityQueue emission for the Bishop code generator.
*/

#include "codegen.hpp"
#include <fmt/format.h>

using namespace std;

namespace codegen {

/**
* Emits a priority queue creation.
* Max heap: PriorityQueue<T>() -> bishop::MaxPriorityQueue<T>()
* Min heap: PriorityQueue<T>.min() -> bishop::MinPriorityQueue<T>()
*/
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<string>& 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
8 changes: 8 additions & 0 deletions codegen/emit_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ string map_type(const string& t) {
return "std::vector<" + map_type(element_type) + ">";
}

// Handle PriorityQueue<T> types: PriorityQueue<int> -> bishop::MaxPriorityQueue<int>
// 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<T> types: Set<int> -> std::unordered_set<int>
if (t.rfind("Set<", 0) == 0 && t.back() == '>') {
string element_type = extract_element_type(t, "Set<");
Expand Down
1 change: 1 addition & 0 deletions lexer/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ static unordered_map<string, TokenType> keywords = {
{"Map", TokenType::MAP},
{"Pair", TokenType::PAIR},
{"Tuple", TokenType::TUPLE},
{"PriorityQueue", TokenType::PRIORITY_QUEUE},
{"Set", TokenType::SET},
{"select", TokenType::SELECT},
{"case", TokenType::CASE},
Expand Down
1 change: 1 addition & 0 deletions lexer/token.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ enum class TokenType {
MAP,
PAIR,
TUPLE,
PRIORITY_QUEUE,
SET,
SELECT,
CASE,
Expand Down
6 changes: 6 additions & 0 deletions parser/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ struct TupleCreate : ASTNode {
vector<unique_ptr<ASTNode>> elements; ///< Element expressions (2-5 elements)
};

/** @brief Priority queue creation: PriorityQueue<T>() or PriorityQueue<T>.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<T>() */
struct SetCreate : ASTNode {
string element_type; ///< Type of elements the set holds
Expand Down
37 changes: 37 additions & 0 deletions parser/parse_primary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,43 @@ unique_ptr<ASTNode> parse_primary(ParserState& state) {
return tuple;
}

// Handle priority queue creation: PriorityQueue<T>() or PriorityQueue<T>.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<Task>
string element_type = parse_type(state);

consume(state, TokenType::GT);

auto pq = make_unique<PriorityQueueCreate>();
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<T> only supports .min() constructor, got '." + method.value + "' at line " + to_string(start_line));
}
} else {
// Default max heap: PriorityQueue<T>()
consume(state, TokenType::LPAREN);
consume(state, TokenType::RPAREN);
}

return pq;
}

// Handle set creation: Set<int>() or Set<str>()
if (check(state, TokenType::SET)) {
int start_line = current(state).line;
Expand Down
9 changes: 9 additions & 0 deletions parser/parse_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ string parse_base_type(ParserState& state) {
return "Tuple<" + element_type + ">";
}

// PriorityQueue<T> 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<T> type
if (check(state, TokenType::SET)) {
advance(state);
Expand Down
Loading