diff --git a/src/ast.nim b/src/ast.nim index fc07eab..c483c62 100644 --- a/src/ast.nim +++ b/src/ast.nim @@ -116,6 +116,7 @@ type ekCast ekIs ekTry + ekUnwrap ## expr! — unwrap or panic ekBlock ekMatch @@ -193,6 +194,8 @@ type of ekTry: exprTryOperand*: Expr exprTryType*: TypeExpr # nil for Result?, or explicit target type + of ekUnwrap: + exprUnwrapOperand*: Expr of ekBlock: exprBlock*: Block of ekMatch: diff --git a/src/hir_lower.nim b/src/hir_lower.nim index 73175fd..feb46a9 100644 --- a/src/hir_lower.nim +++ b/src/hir_lower.nim @@ -373,6 +373,8 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type = of ekTry: # For now, assume Result -> int or Option -> int return makeInt() + of ekUnwrap: + return makeInt() of ekBlock: if expr.exprBlock.stmts.len > 0: let last = expr.exprBlock.stmts[^1] @@ -698,6 +700,51 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode = ctx.pendingStmts.add(ifNode) return okLoad + of ekUnwrap: + let operand = ctx.lowerExpr(expr.exprUnwrapOperand) + let operandType = ctx.resolveExprType(expr.exprUnwrapOperand) + + var errTag = "Result_Err" + var typeName = "Result" + if operandType.kind == tkNamed: + typeName = operandType.name + if typeName == "Option": + errTag = "Option_None" + + let tmpName = ctx.freshTryVar() + let tmpAlloca = hirAlloca(tmpName, operandType, loc) + let tmpVar = hirVar(tmpName, makePointer(operandType), loc) + let tmpStore = hirStore(tmpVar, operand, loc) + + let tagPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "tag", + typ: makePointer(makeNamed(typeName & "_Tag")), loc: loc) + let tagLoad = HirNode(kind: hLoad, loadPtr: tagPtr, + typ: makeNamed(typeName & "_Tag"), loc: loc) + let errConst = hirVar(errTag, makeNamed(typeName & "_Tag"), loc) + let cond = hirBinary(tkEq, tagLoad, errConst, makeBool(), loc) + + # On error: call bux_panic("unwrap failed") + let panicTok = Token(kind: tkStringLiteral, text: "\"unwrap failed\"", loc: loc) + let panicMsg = HirNode(kind: hLit, litToken: panicTok, typ: makeStr(), loc: loc) + let panicCall = hirCall("bux_panic", @[panicMsg], makeVoid(), loc) + let thenBlock = hirBlock(@[panicCall], nil, makeVoid(), loc) + let ifNode = HirNode(kind: hIf, ifCond: cond, ifThen: thenBlock, + ifElse: nil, typ: makeVoid(), loc: loc) + + # Extract the Ok/Some value + let dataPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "data", + typ: makePointer(makeNamed(typeName & "_Data")), loc: loc) + let dataLoad = HirNode(kind: hLoad, loadPtr: dataPtr, + typ: makeNamed(typeName & "_Data"), loc: loc) + let okPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: "Ok_0", + typ: makePointer(makeInt()), loc: loc) + let okLoad = HirNode(kind: hLoad, loadPtr: okPtr, typ: makeInt(), loc: loc) + + ctx.pendingStmts.add(tmpAlloca) + ctx.pendingStmts.add(tmpStore) + ctx.pendingStmts.add(ifNode) + return okLoad + of ekMatch: let subject = ctx.lowerExpr(expr.exprMatchSubject) var arms: seq[HirMatchArm] = @[] @@ -1065,6 +1112,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule = result.add(findGenericCalls(expr.exprUnaryOperand)) of ekTry: result.add(findGenericCalls(expr.exprTryOperand)) + of ekUnwrap: + result.add(findGenericCalls(expr.exprUnwrapOperand)) of ekAssign: result.add(findGenericCalls(expr.exprAssignTarget)) result.add(findGenericCalls(expr.exprAssignValue)) diff --git a/src/parser.nim b/src/parser.nim index d43331f..0bd84b3 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -508,6 +508,9 @@ proc parsePostfix(p: var Parser): Expr = of tkQuestion: discard p.advance() left = Expr(kind: ekTry, loc: loc, exprTryOperand: left, exprTryType: nil) + of tkBang: + discard p.advance() + left = Expr(kind: ekUnwrap, loc: loc, exprUnwrapOperand: left) of tkLBrace: if p.structInitAllowed and left.kind in {ekIdent, ekPath, ekGenericCall}: discard p.advance() diff --git a/src/sema.nim b/src/sema.nim index 1756c5d..07b694a 100644 --- a/src/sema.nim +++ b/src/sema.nim @@ -734,6 +734,10 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type = # For now, assume Result -> int # TODO: check operand is Result/Option and current function returns same type return makeInt() + of ekUnwrap: + let operandType = sema.checkExpr(expr.exprUnwrapOperand, scope) + # Unwrap: extract Ok value or panic on Err + return makeInt() of ekBlock: var blockScope = newScope(scope) var lastType = makeVoid() diff --git a/tests/hir_test b/tests/hir_test index 942a0fe..74cfee1 100755 Binary files a/tests/hir_test and b/tests/hir_test differ diff --git a/tests/parser_test b/tests/parser_test index f4a2f9f..56c0f17 100755 Binary files a/tests/parser_test and b/tests/parser_test differ diff --git a/tests/sema_test b/tests/sema_test index bc4c51e..604d73b 100755 Binary files a/tests/sema_test and b/tests/sema_test differ