Skip to content

Commit 592240e

Browse files
mjm918claude
andcommitted
feat(options): redesign option handling with var...else and or_default
Replace Rust-style option handling with cleaner naml API: - Add `var x = opt else { ... }` syntax for unwrapping with early return - Add `.or_default(value)` method for providing defaults - Remove `.unwrap()` and `.unwrap_or()` methods - Keep `.is_some()` and `.is_none()` for boolean checks Also fix parser to support `::` path syntax for enum variants (e.g., `UserRole::Admin` now parses correctly as PathExpr). Changes: - AST: Add else_block field to VarStmt - Parser: Parse else block after var initializer, parse :: paths - Type checker: Validate else clause, add or_default, remove unwrap - Codegen: Generate match expression for var...else pattern Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bf7d852 commit 592240e

File tree

8 files changed

+330
-179
lines changed

8 files changed

+330
-179
lines changed

β€Žexamples/simple.namlβ€Ž

Lines changed: 81 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,6 @@ interface Serializable {
3434
fn to_bytes() -> bytes;
3535
}
3636

37-
// =============================================================================
38-
// Result and Option Types
39-
// =============================================================================
40-
41-
enum Option<T> {
42-
Some(T),
43-
None
44-
}
4537

4638
// =============================================================================
4739
// Error Types
@@ -151,10 +143,10 @@ pub struct PhoneNumber {
151143

152144
pub fn (self: PhoneNumber) format() -> string {
153145
var base: string = "+" + self.country_code + " (" + self.area_code + ") " + self.number;
154-
if (self.extension.is_some()) {
155-
return base + " ext. " + self.extension.unwrap();
146+
var ext: string = self.extension else {
147+
return base;
156148
}
157-
return base;
149+
return base + " ext. " + ext;
158150
}
159151

160152
enum UserStatus {
@@ -191,14 +183,9 @@ pub struct User implements Serializable {
191183
}
192184

193185
pub fn (self: User) full_name() -> string {
194-
var first: string = "";
195-
var last: string = "";
196-
if (self.first_name.is_some()) {
197-
first = self.first_name.unwrap();
198-
}
199-
if (self.last_name.is_some()) {
200-
last = self.last_name.unwrap();
201-
}
186+
var first: string = self.first_name.or_default("");
187+
var last: string = self.last_name.or_default("");
188+
202189
if (first == "" && last == "") {
203190
return self.username;
204191
}
@@ -232,7 +219,7 @@ pub fn (self: User) has_role(role: UserRole) -> bool {
232219
}
233220

234221
pub fn (self: User) is_admin() -> bool {
235-
return self.has_role(UserRole.Admin);
222+
return self.has_role(UserRole::Admin);
236223
}
237224

238225
pub fn (self: User) to_json() -> string {
@@ -272,7 +259,10 @@ pub fn (mut self: LinkedList<T>) add(item: T) {
272259
self.head = some(node);
273260
self.tail = some(node);
274261
} else {
275-
self.tail.unwrap().next = some(node);
262+
var tail_node: LinkedListNode<T> = self.tail else {
263+
return;
264+
}
265+
tail_node.next = some(node);
276266
self.tail = some(node);
277267
}
278268
self.length = self.length + 1;
@@ -281,10 +271,13 @@ pub fn (mut self: LinkedList<T>) add(item: T) {
281271
pub fn (self: LinkedList<T>) contains(item: T) -> bool {
282272
var current: option<LinkedListNode<T>> = self.head;
283273
while (current.is_some()) {
284-
if (current.unwrap().value == item) {
274+
var node: LinkedListNode<T> = current else {
275+
break;
276+
}
277+
if (node.value == item) {
285278
return true;
286279
}
287-
current = current.unwrap().next;
280+
current = node.next;
288281
}
289282
return false;
290283
}
@@ -293,20 +286,31 @@ pub fn (mut self: LinkedList<T>) remove(item: T) -> bool {
293286
if (self.head.is_none()) {
294287
return false;
295288
}
296-
if (self.head.unwrap().value == item) {
297-
self.head = self.head.unwrap().next;
289+
var head_node: LinkedListNode<T> = self.head else {
290+
return false;
291+
}
292+
if (head_node.value == item) {
293+
self.head = head_node.next;
298294
self.length = self.length - 1;
299295
return true;
300296
}
301297
var current: option<LinkedListNode<T>> = self.head;
302-
while (current.unwrap().next.is_some()) {
303-
if (current.unwrap().next.unwrap().value == item) {
304-
var node: LinkedListNode<T> = current.unwrap();
305-
node.next = current.unwrap().next.unwrap().next;
298+
while (current.is_some()) {
299+
var curr_node: LinkedListNode<T> = current else {
300+
break;
301+
}
302+
if (curr_node.next.is_none()) {
303+
break;
304+
}
305+
var next_node: LinkedListNode<T> = curr_node.next else {
306+
break;
307+
}
308+
if (next_node.value == item) {
309+
curr_node.next = next_node.next;
306310
self.length = self.length - 1;
307311
return true;
308312
}
309-
current = current.unwrap().next;
313+
current = curr_node.next;
310314
}
311315
return false;
312316
}
@@ -332,10 +336,16 @@ pub fn (self: TreeNode<T>) balance_factor() -> int {
332336
var left_height: int = 0;
333337
var right_height: int = 0;
334338
if (self.left.is_some()) {
335-
left_height = self.left.unwrap().height;
339+
var left_node: TreeNode<T> = self.left else {
340+
return 0;
341+
}
342+
left_height = left_node.height;
336343
}
337344
if (self.right.is_some()) {
338-
right_height = self.right.unwrap().height;
345+
var right_node: TreeNode<T> = self.right else {
346+
return left_height;
347+
}
348+
right_height = right_node.height;
339349
}
340350
return left_height - right_height;
341351
}
@@ -350,7 +360,10 @@ pub fn (mut self: BinarySearchTree<T>) insert(value: T) {
350360
if (self.root.is_none()) {
351361
self.root = some(node);
352362
} else {
353-
self.insert_recursive(self.root.unwrap(), value);
363+
var root_node: TreeNode<T> = self.root else {
364+
return;
365+
}
366+
self.insert_recursive(root_node, value);
354367
}
355368
self.size = self.size + 1;
356369
}
@@ -359,7 +372,10 @@ fn (self: BinarySearchTree<T>) insert_recursive(current: TreeNode<T>, value: T)
359372
if (current.left.is_none()) {
360373
current.left = some(TreeNode { value: value, left: none, right: none, height: 1 });
361374
} else {
362-
self.insert_recursive(current.left.unwrap(), value);
375+
var left_node: TreeNode<T> = current.left else {
376+
return;
377+
}
378+
self.insert_recursive(left_node, value);
363379
}
364380
self.update_height(current);
365381
}
@@ -368,10 +384,16 @@ fn (self: BinarySearchTree<T>) update_height(node: TreeNode<T>) {
368384
var left_height: int = 0;
369385
var right_height: int = 0;
370386
if (node.left.is_some()) {
371-
left_height = node.left.unwrap().height;
387+
var left_node: TreeNode<T> = node.left else {
388+
return;
389+
}
390+
left_height = left_node.height;
372391
}
373392
if (node.right.is_some()) {
374-
right_height = node.right.unwrap().height;
393+
var right_node: TreeNode<T> = node.right else {
394+
return;
395+
}
396+
right_height = right_node.height;
375397
}
376398
if (left_height > right_height) {
377399
node.height = left_height + 1;
@@ -388,7 +410,10 @@ fn (self: BinarySearchTree<T>) find_recursive(current: option<TreeNode<T>>, valu
388410
if (current.is_none()) {
389411
return none;
390412
}
391-
return self.find_recursive(current.unwrap().left, value);
413+
var node: TreeNode<T> = current else {
414+
return none;
415+
}
416+
return self.find_recursive(node.left, value);
392417
}
393418

394419
// =============================================================================
@@ -603,13 +628,15 @@ pub async fn (self: UserRepository) delete(id: UserId) -> bool throws DatabaseEr
603628
pub async fn (self: UserRepository) exists(id: UserId) -> bool throws DatabaseError {
604629
var query: string = "SELECT COUNT(*) FROM " + self.table_name + " WHERE id = ?";
605630
var result: [map<string, string>] = await self.execute_query(query, [id.value]);
606-
return result[0]["count"].unwrap() > "0";
631+
var count_str: string = result[0]["count"].or_default("0");
632+
return count_str > "0";
607633
}
608634

609635
pub async fn (self: UserRepository) count() -> int throws DatabaseError {
610636
var query: string = "SELECT COUNT(*) as cnt FROM " + self.table_name;
611637
var result: [map<string, string>] = await self.execute_query(query, []);
612-
return result[0]["cnt"].unwrap() as int;
638+
var cnt_str: string = result[0]["cnt"].or_default("0");
639+
return cnt_str as int;
613640
}
614641

615642
async fn (self: UserRepository) execute_query(query: string, params: [string]) -> [map<string, string>] throws DatabaseError {
@@ -621,17 +648,19 @@ async fn (self: UserRepository) execute_update(query: string, params: [string])
621648
}
622649

623650
fn (self: UserRepository) map_row_to_user(row: map<string, string>) -> User {
651+
var id_str: string = row["id"].or_default("");
652+
var username_str: string = row["username"].or_default("");
624653
return User {
625-
id: UserId { value: row["id"].unwrap() },
654+
id: UserId { value: id_str },
626655
email: Email { local: "user", domain: "example.com" },
627-
username: row["username"].unwrap(),
656+
username: username_str,
628657
password_hash: "",
629658
first_name: none,
630659
last_name: none,
631660
address: none,
632661
phone: none,
633662
status: UserStatus.Active,
634-
roles: [UserRole.User],
663+
roles: [UserRole::User],
635664
metadata: {},
636665
created_at: 0,
637666
updated_at: 0,
@@ -881,17 +910,17 @@ async fn main() {
881910

882911
var user_id: UserId = UserId { value: "user-123" };
883912

884-
var user: option<User> = await user_repo.find_by_id(user_id);
913+
var user_opt: option<User> = await user_repo.find_by_id(user_id);
885914

886-
if (user.is_some()) {
887-
printf("Found user: {}", user.unwrap().full_name());
888-
889-
var response: HttpResponse = await client.get("/users/" + user_id.value);
890-
if (response.is_success()) {
891-
printf("API call successful: {} {}", response.status_code, response.status_text);
892-
}
893-
} else {
915+
var user: User = user_opt else {
894916
printf("User not found");
917+
return;
918+
}
919+
printf("Found user: {}", user.full_name());
920+
921+
var response: HttpResponse = await client.get("/users/" + user_id.value);
922+
if (response.is_success()) {
923+
printf("API call successful: {} {}", response.status_code, response.status_text);
895924
}
896925

897926
var numbers: [int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@@ -943,4 +972,4 @@ async fn main() {
943972
var response: HttpResponse = await task;
944973
printf("Task completed: {}", response.status_code);
945974
}
946-
}
975+
}

0 commit comments

Comments
Β (0)