feat: add range-based for loops
- Parse range expressions in expressions (0..5, 0..=5) - Add ekRange AST node, hRange HIR node - Lower for-range to while loop with alloca counter - Add hBlock.isScope flag for C scope emission - Update C backend to emit scope blocks only when isScope is set
This commit is contained in:
+10
-4
@@ -307,11 +307,17 @@ proc emitStmt(be: var CBackend, node: HirNode) =
|
|||||||
be.emitLine("continue;")
|
be.emitLine("continue;")
|
||||||
|
|
||||||
of hBlock:
|
of hBlock:
|
||||||
|
if node.isScope:
|
||||||
|
be.emitLine("{")
|
||||||
|
inc be.indent
|
||||||
for stmt in node.blockStmts:
|
for stmt in node.blockStmts:
|
||||||
be.emitStmt(stmt)
|
be.emitStmt(stmt)
|
||||||
if node.blockExpr != nil:
|
if node.blockExpr != nil:
|
||||||
let val = be.emitExpr(node.blockExpr)
|
let val = be.emitExpr(node.blockExpr)
|
||||||
be.emitLine(&"{val};")
|
be.emitLine(&"{val};")
|
||||||
|
if node.isScope:
|
||||||
|
dec be.indent
|
||||||
|
be.emitLine("}")
|
||||||
|
|
||||||
of hAlloca:
|
of hAlloca:
|
||||||
let typ = typeToC(node.allocaType)
|
let typ = typeToC(node.allocaType)
|
||||||
@@ -352,12 +358,12 @@ proc emitFunc*(be: var CBackend, hfunc: HirFunc) =
|
|||||||
be.emitLine(&"{retType} {hfunc.name}({paramsStr}) {{")
|
be.emitLine(&"{retType} {hfunc.name}({paramsStr}) {{")
|
||||||
inc be.indent
|
inc be.indent
|
||||||
if hfunc.body != nil:
|
if hfunc.body != nil:
|
||||||
if hfunc.body.kind == hBlock and hfunc.body.blockExpr != nil and hfunc.retType.kind != tkVoid:
|
if hfunc.body.kind == hBlock:
|
||||||
# Function returns a value via block expression — emit statements and add return
|
|
||||||
for stmt in hfunc.body.blockStmts:
|
for stmt in hfunc.body.blockStmts:
|
||||||
be.emitStmt(stmt)
|
be.emitStmt(stmt)
|
||||||
let val = be.emitExpr(hfunc.body.blockExpr)
|
if hfunc.body.blockExpr != nil and hfunc.retType.kind != tkVoid:
|
||||||
be.emitLine(&"return {val};")
|
let val = be.emitExpr(hfunc.body.blockExpr)
|
||||||
|
be.emitLine(&"return {val};")
|
||||||
else:
|
else:
|
||||||
be.emitStmt(hfunc.body)
|
be.emitStmt(hfunc.body)
|
||||||
dec be.indent
|
dec be.indent
|
||||||
|
|||||||
+8
-2
@@ -34,6 +34,7 @@ type
|
|||||||
hBlock
|
hBlock
|
||||||
hStructInit
|
hStructInit
|
||||||
hSliceInit
|
hSliceInit
|
||||||
|
hRange
|
||||||
hTupleInit
|
hTupleInit
|
||||||
# Match (desugared to switch/branch later)
|
# Match (desugared to switch/branch later)
|
||||||
hMatch
|
hMatch
|
||||||
@@ -106,11 +107,16 @@ type
|
|||||||
of hBlock:
|
of hBlock:
|
||||||
blockStmts*: seq[HirNode]
|
blockStmts*: seq[HirNode]
|
||||||
blockExpr*: HirNode
|
blockExpr*: HirNode
|
||||||
|
isScope*: bool
|
||||||
of hStructInit:
|
of hStructInit:
|
||||||
structInitName*: string
|
structInitName*: string
|
||||||
structInitFields*: seq[tuple[name: string, value: HirNode]]
|
structInitFields*: seq[tuple[name: string, value: HirNode]]
|
||||||
of hSliceInit:
|
of hSliceInit:
|
||||||
sliceInitElements*: seq[HirNode]
|
sliceInitElements*: seq[HirNode]
|
||||||
|
of hRange:
|
||||||
|
rangeLo*: HirNode
|
||||||
|
rangeHi*: HirNode
|
||||||
|
rangeInclusive*: bool
|
||||||
of hTupleInit:
|
of hTupleInit:
|
||||||
tupleInitElements*: seq[HirNode]
|
tupleInitElements*: seq[HirNode]
|
||||||
of hMatch:
|
of hMatch:
|
||||||
@@ -162,8 +168,8 @@ proc hirCall*(callee: string, args: seq[HirNode], typ: Type, loc: SourceLocation
|
|||||||
proc hirReturn*(value: HirNode, loc: SourceLocation): HirNode =
|
proc hirReturn*(value: HirNode, loc: SourceLocation): HirNode =
|
||||||
HirNode(kind: hReturn, returnValue: value, typ: makeVoid(), loc: loc)
|
HirNode(kind: hReturn, returnValue: value, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
proc hirBlock*(stmts: seq[HirNode], expr: HirNode, typ: Type, loc: SourceLocation): HirNode =
|
proc hirBlock*(stmts: seq[HirNode], expr: HirNode, typ: Type, loc: SourceLocation, isScope: bool = false): HirNode =
|
||||||
HirNode(kind: hBlock, blockStmts: stmts, blockExpr: expr, typ: typ, loc: loc)
|
HirNode(kind: hBlock, blockStmts: stmts, blockExpr: expr, typ: typ, loc: loc, isScope: isScope)
|
||||||
|
|
||||||
proc hirAlloca*(name: string, typ: Type, loc: SourceLocation): HirNode =
|
proc hirAlloca*(name: string, typ: Type, loc: SourceLocation): HirNode =
|
||||||
HirNode(kind: hAlloca, allocaType: typ, allocaName: name, typ: makePointer(typ), loc: loc)
|
HirNode(kind: hAlloca, allocaType: typ, allocaName: name, typ: makePointer(typ), loc: loc)
|
||||||
|
|||||||
+56
-5
@@ -229,6 +229,9 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
if expr.exprSliceElements.len > 0:
|
if expr.exprSliceElements.len > 0:
|
||||||
return makeSlice(ctx.resolveExprType(expr.exprSliceElements[0]))
|
return makeSlice(ctx.resolveExprType(expr.exprSliceElements[0]))
|
||||||
return makeSlice(makeUnknown())
|
return makeSlice(makeUnknown())
|
||||||
|
of ekRange:
|
||||||
|
let loType = ctx.resolveExprType(expr.exprRangeLo)
|
||||||
|
return makeRange(loType)
|
||||||
of ekTuple:
|
of ekTuple:
|
||||||
var elems: seq[Type] = @[]
|
var elems: seq[Type] = @[]
|
||||||
for e in expr.exprTupleElements:
|
for e in expr.exprTupleElements:
|
||||||
@@ -380,6 +383,12 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
elems.add(ctx.lowerExpr(e))
|
elems.add(ctx.lowerExpr(e))
|
||||||
return HirNode(kind: hSliceInit, sliceInitElements: elems, typ: typ, loc: loc)
|
return HirNode(kind: hSliceInit, sliceInitElements: elems, typ: typ, loc: loc)
|
||||||
|
|
||||||
|
of ekRange:
|
||||||
|
let lo = ctx.lowerExpr(expr.exprRangeLo)
|
||||||
|
let hi = ctx.lowerExpr(expr.exprRangeHi)
|
||||||
|
return HirNode(kind: hRange, rangeLo: lo, rangeHi: hi,
|
||||||
|
rangeInclusive: expr.exprRangeInclusive, typ: typ, loc: loc)
|
||||||
|
|
||||||
of ekTuple:
|
of ekTuple:
|
||||||
var elems: seq[HirNode] = @[]
|
var elems: seq[HirNode] = @[]
|
||||||
for e in expr.exprTupleElements:
|
for e in expr.exprTupleElements:
|
||||||
@@ -509,11 +518,53 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
of skFor:
|
of skFor:
|
||||||
# Desugar: for i in iter { body } → { let __iter = iter; while __hasNext(__iter) { let i = __next(__iter); body } }
|
let iterExpr = stmt.stmtForIter
|
||||||
let iterExpr = ctx.lowerExpr(stmt.stmtForIter)
|
let body = stmt.stmtForBody
|
||||||
let body = ctx.lowerBlock(stmt.stmtForBody)
|
let varName = stmt.stmtForVar
|
||||||
# Simplified: just lower the body for now
|
let loc = stmt.loc
|
||||||
return HirNode(kind: hLoop, loopBody: body, typ: makeVoid(), loc: loc)
|
|
||||||
|
# Range-based for: for i in lo..hi { body }
|
||||||
|
if iterExpr.kind == ekRange:
|
||||||
|
let lo = ctx.lowerExpr(iterExpr.exprRangeLo)
|
||||||
|
let hi = ctx.lowerExpr(iterExpr.exprRangeHi)
|
||||||
|
let inclusive = iterExpr.exprRangeInclusive
|
||||||
|
|
||||||
|
# Determine loop variable type from range bounds
|
||||||
|
let varType = ctx.resolveExprType(iterExpr.exprRangeLo)
|
||||||
|
|
||||||
|
# Create: var i = lo; while i < hi { body; i = i + 1; }
|
||||||
|
let initStmt = hirAlloca(varName, varType, loc)
|
||||||
|
let varNode = hirVar(varName, makePointer(varType), loc)
|
||||||
|
let initStore = hirStore(varNode, lo, loc)
|
||||||
|
|
||||||
|
let readI = hirVar(varName, varType, loc)
|
||||||
|
let condOp = if inclusive: tkLe else: tkLt
|
||||||
|
let cond = HirNode(kind: hBinary, binaryOp: condOp,
|
||||||
|
binaryLeft: readI, binaryRight: hi,
|
||||||
|
typ: makeBool(), loc: loc)
|
||||||
|
|
||||||
|
var bodyStmts: seq[HirNode] = @[]
|
||||||
|
bodyStmts.add(ctx.lowerBlock(body))
|
||||||
|
|
||||||
|
let readI2 = hirVar(varName, varType, loc)
|
||||||
|
let one = hirLit(Token(kind: tkIntLiteral, text: "1", loc: loc), varType, loc)
|
||||||
|
let inc = HirNode(kind: hBinary, binaryOp: tkPlus,
|
||||||
|
binaryLeft: readI2, binaryRight: one,
|
||||||
|
typ: varType, loc: loc)
|
||||||
|
bodyStmts.add(hirStore(varNode, inc, loc))
|
||||||
|
|
||||||
|
let whileBody = hirBlock(bodyStmts, nil, makeVoid(), loc)
|
||||||
|
let whileNode = HirNode(kind: hWhile, whileCond: cond, whileBody: whileBody,
|
||||||
|
typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
|
# Wrap in a block so loop variable doesn't leak into outer scope
|
||||||
|
let forBlock = hirBlock(@[initStmt, initStore, whileNode], nil, makeVoid(), loc, isScope = true)
|
||||||
|
return forBlock
|
||||||
|
|
||||||
|
# Generic iterator for loop (simplified - just infinite loop for now)
|
||||||
|
let loweredIter = ctx.lowerExpr(iterExpr)
|
||||||
|
let loweredBody = ctx.lowerBlock(body)
|
||||||
|
return HirNode(kind: hLoop, loopBody: loweredBody, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
of skDoWhile:
|
of skDoWhile:
|
||||||
let body = ctx.lowerBlock(stmt.stmtDoWhileBody)
|
let body = ctx.lowerBlock(stmt.stmtDoWhileBody)
|
||||||
|
|||||||
+13
-2
@@ -326,6 +326,7 @@ proc parseAdd(p: var Parser): Expr
|
|||||||
proc parseShift(p: var Parser): Expr
|
proc parseShift(p: var Parser): Expr
|
||||||
proc parseCast(p: var Parser): Expr
|
proc parseCast(p: var Parser): Expr
|
||||||
proc parseComparison(p: var Parser): Expr
|
proc parseComparison(p: var Parser): Expr
|
||||||
|
proc parseRange(p: var Parser): Expr
|
||||||
proc parseEquality(p: var Parser): Expr
|
proc parseEquality(p: var Parser): Expr
|
||||||
proc parseBitAnd(p: var Parser): Expr
|
proc parseBitAnd(p: var Parser): Expr
|
||||||
proc parseBitXor(p: var Parser): Expr
|
proc parseBitXor(p: var Parser): Expr
|
||||||
@@ -583,12 +584,22 @@ proc parseComparison(p: var Parser): Expr =
|
|||||||
left = newBinaryExpr(op, left, right, loc)
|
left = newBinaryExpr(op, left, right, loc)
|
||||||
return left
|
return left
|
||||||
|
|
||||||
proc parseEquality(p: var Parser): Expr =
|
proc parseRange(p: var Parser): Expr =
|
||||||
let loc = p.currentLoc
|
let loc = p.currentLoc
|
||||||
var left = p.parseComparison()
|
var left = p.parseComparison()
|
||||||
|
if p.check(tkDotDot) or p.check(tkDotDotEqual):
|
||||||
|
let inclusive = p.check(tkDotDotEqual)
|
||||||
|
discard p.advance()
|
||||||
|
let right = p.parseComparison()
|
||||||
|
return Expr(kind: ekRange, loc: loc, exprRangeLo: left, exprRangeHi: right, exprRangeInclusive: inclusive)
|
||||||
|
return left
|
||||||
|
|
||||||
|
proc parseEquality(p: var Parser): Expr =
|
||||||
|
let loc = p.currentLoc
|
||||||
|
var left = p.parseRange()
|
||||||
while p.checkAny([tkEq, tkNe]):
|
while p.checkAny([tkEq, tkNe]):
|
||||||
let op = p.advance().kind
|
let op = p.advance().kind
|
||||||
let right = p.parseComparison()
|
let right = p.parseRange()
|
||||||
left = newBinaryExpr(op, left, right, loc)
|
left = newBinaryExpr(op, left, right, loc)
|
||||||
return left
|
return left
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user