feat: switch/case statement
- Add syntax - Desugars to if-else chain in HIR lowering (both compilers) - No new HIR/C backend nodes needed — reuses hIf/hBinary/hBlock - Supports integer, char, and enum tag dispatch - Case body can be single statement (no braces required)
This commit is contained in:
@@ -239,6 +239,7 @@ type
|
||||
skComptime
|
||||
skEmit
|
||||
skDefer
|
||||
skSwitch
|
||||
skDecl
|
||||
|
||||
ElseIf* = object
|
||||
@@ -246,6 +247,11 @@ type
|
||||
cond*: Expr
|
||||
blk*: Block
|
||||
|
||||
SwitchCase* = object
|
||||
loc*: SourceLocation
|
||||
caseValue*: Expr
|
||||
caseBody*: Block
|
||||
|
||||
Block* = ref object
|
||||
loc*: SourceLocation
|
||||
stmts*: seq[Stmt]
|
||||
@@ -301,6 +307,10 @@ type
|
||||
stmtEmitEvaluated*: string ## filled by sema CTFE
|
||||
of skDefer:
|
||||
stmtDeferBody*: Expr
|
||||
of skSwitch:
|
||||
stmtSwitchExpr*: Expr
|
||||
stmtSwitchCases*: seq[SwitchCase]
|
||||
stmtSwitchDefault*: Block
|
||||
of skDecl:
|
||||
stmtDecl*: Decl
|
||||
|
||||
|
||||
@@ -1127,6 +1127,24 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
||||
return ctx.flushPending(HirNode(kind: hMatch, matchSubject: subject, matchArms: arms,
|
||||
typ: makeVoid(), loc: loc))
|
||||
|
||||
of skSwitch:
|
||||
let subject = ctx.lowerExpr(stmt.stmtSwitchExpr)
|
||||
var current: HirNode = nil
|
||||
# Build if-else chain from bottom up (default first)
|
||||
if stmt.stmtSwitchDefault != nil:
|
||||
current = ctx.lowerBlock(stmt.stmtSwitchDefault)
|
||||
# Cases in reverse order
|
||||
for i in countdown(stmt.stmtSwitchCases.len - 1, 0):
|
||||
let caseBranch = stmt.stmtSwitchCases[i]
|
||||
let caseVal = ctx.lowerExpr(caseBranch.caseValue)
|
||||
let caseBody = ctx.lowerBlock(caseBranch.caseBody)
|
||||
let cond = HirNode(kind: hBinary, binaryOp: tkEq,
|
||||
binaryLeft: subject, binaryRight: caseVal,
|
||||
typ: makeBool(), loc: caseBranch.loc)
|
||||
current = HirNode(kind: hIf, ifCond: cond, ifThen: caseBody, ifElse: current,
|
||||
typ: makeVoid(), loc: caseBranch.loc)
|
||||
return ctx.flushPending(current)
|
||||
|
||||
of skDefer:
|
||||
let body = ctx.lowerExpr(stmt.stmtDeferBody)
|
||||
ctx.deferStmts.add(body)
|
||||
|
||||
@@ -901,6 +901,37 @@ proc parseStmt(p: var Parser): Stmt =
|
||||
discard p.advance()
|
||||
discard p.expect(tkRBrace, "expected '}' to close match")
|
||||
return Stmt(kind: skExpr, loc: loc, stmtExpr: Expr(kind: ekMatch, loc: loc, exprMatchSubject: subject, exprMatchArms: arms))
|
||||
of tkSwitch:
|
||||
discard p.advance()
|
||||
p.structInitAllowed = false
|
||||
let subject = p.parseExpr()
|
||||
p.structInitAllowed = true
|
||||
while p.check(tkNewLine):
|
||||
discard p.advance()
|
||||
discard p.expect(tkLBrace, "expected '{' to start switch body")
|
||||
var cases: seq[SwitchCase] = @[]
|
||||
var defaultBlock: Block = nil
|
||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||
while p.check(tkNewLine):
|
||||
discard p.advance()
|
||||
if p.check(tkRBrace) or p.isAtEnd:
|
||||
break
|
||||
if p.check(tkDefault):
|
||||
discard p.advance()
|
||||
discard p.expect(tkColon, "expected ':' after default")
|
||||
defaultBlock = Block(loc: p.currentLoc, stmts: @[p.parseStmt()])
|
||||
continue
|
||||
if p.check(tkCase):
|
||||
discard p.advance()
|
||||
let caseVal = p.parseExpr()
|
||||
discard p.expect(tkColon, "expected ':' after case value")
|
||||
let caseBody = Block(loc: p.currentLoc, stmts: @[p.parseStmt()])
|
||||
cases.add(SwitchCase(loc: caseVal.loc, caseValue: caseVal, caseBody: caseBody))
|
||||
continue
|
||||
# Unknown token in switch body — skip
|
||||
discard p.advance()
|
||||
discard p.expect(tkRBrace, "expected '}' to close switch")
|
||||
return Stmt(kind: skSwitch, loc: loc, stmtSwitchExpr: subject, stmtSwitchCases: cases, stmtSwitchDefault: defaultBlock)
|
||||
of tkReturn:
|
||||
discard p.advance()
|
||||
var val: Expr = nil
|
||||
|
||||
@@ -1338,6 +1338,14 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
|
||||
of skDefer:
|
||||
discard sema.checkExpr(stmt.stmtDeferBody, scope)
|
||||
return makeVoid()
|
||||
of skSwitch:
|
||||
discard sema.checkExpr(stmt.stmtSwitchExpr, scope)
|
||||
for caseBranch in stmt.stmtSwitchCases:
|
||||
discard sema.checkExpr(caseBranch.caseValue, scope)
|
||||
discard sema.checkStmt(Stmt(kind: skExpr, loc: caseBranch.caseBody.loc, stmtExpr: Expr(kind: ekBlock, loc: caseBranch.caseBody.loc, exprBlock: caseBranch.caseBody)), scope)
|
||||
if stmt.stmtSwitchDefault != nil:
|
||||
discard sema.checkStmt(Stmt(kind: skExpr, loc: stmt.stmtSwitchDefault.loc, stmtExpr: Expr(kind: ekBlock, loc: stmt.stmtSwitchDefault.loc, exprBlock: stmt.stmtSwitchDefault)), scope)
|
||||
return makeVoid()
|
||||
of skDecl:
|
||||
# Local declaration inside block
|
||||
case stmt.stmtDecl.kind
|
||||
|
||||
+10
-1
@@ -25,6 +25,9 @@ type
|
||||
tkContinue # continue
|
||||
tkReturn # return
|
||||
tkMatch # match
|
||||
tkSwitch # switch
|
||||
tkCase # case
|
||||
tkDefault # default
|
||||
|
||||
##Declaration keywords
|
||||
tkFunc # func
|
||||
@@ -150,7 +153,7 @@ type
|
||||
proc isKeyword*(kind: TokenKind): bool =
|
||||
case kind
|
||||
of tkIf, tkElse, tkWhile, tkDo, tkLoop, tkFor, tkIn,
|
||||
tkBreak, tkContinue, tkReturn, tkMatch,
|
||||
tkBreak, tkContinue, tkReturn, tkMatch, tkSwitch, tkCase, tkDefault,
|
||||
tkFunc, tkLet, tkVar, tkConst, tkType, tkStruct, tkEnum,
|
||||
tkUnion, tkInterface, tkExtend, tkModule, tkImport,
|
||||
tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper, tkOwn, tkMut, tkBorrow, tkDiscard, tkAsync, tkAwait, tkSpawn, tkStaticAssert, tkComptime, tkDyn, tkDefer:
|
||||
@@ -201,6 +204,9 @@ proc keywordKind*(text: string): TokenKind =
|
||||
of "continue": tkContinue
|
||||
of "return": tkReturn
|
||||
of "match": tkMatch
|
||||
of "switch": tkSwitch
|
||||
of "case": tkCase
|
||||
of "default": tkDefault
|
||||
of "as": tkAs
|
||||
of "is": tkIs
|
||||
of "null": tkNull
|
||||
@@ -242,6 +248,9 @@ proc tokenKindName*(kind: TokenKind): string =
|
||||
of tkContinue: "'continue'"
|
||||
of tkReturn: "'return'"
|
||||
of tkMatch: "'match'"
|
||||
of tkSwitch: "'switch'"
|
||||
of tkCase: "'case'"
|
||||
of tkDefault: "'default'"
|
||||
of tkFunc: "'func'"
|
||||
of tkLet: "'let'"
|
||||
of tkVar: "'var'"
|
||||
|
||||
Reference in New Issue
Block a user