Skip to content

Commit

Permalink
Support variable scopes for sub-blocks.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Oct 5, 2023
1 parent bf73c50 commit 0614085
Show file tree
Hide file tree
Showing 21 changed files with 856 additions and 883 deletions.
22 changes: 9 additions & 13 deletions docs/hugo/content/docs/toc/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,18 @@ var a = 123
a = 234
```

A new variable can be declared in function blocks with the same name as a variable from a parent block.
Blocks create a new variable scope. Variables declared in the current scope will take precedence over any parent variables with the same name.
```cy
var a = 123
foo = func():
-- A new local `a` inside function `foo`.
func foo():
-- `a` declared inside `foo`.
var a = 234
foo()
print a -- '123'
```

However, variables declared in sub-blocks such as `if` and `for` can not shadow variables in the same main/function block.
```cy
var a = 123
if true:
-- CompileError, `a` is already declared in the main block.
var a = 234
if true:
-- A new `a` declared inside `if`.
var a = 345
print a -- Prints "345"

print a -- Prints "234"
```

When a parent local is referenced in a [lambda function]({{<relref "/docs/toc/functions#lambdas">}}), the variable is automatically captured. Note that [static functions]({{<relref "/docs/toc/functions#static-functions">}}) can not capture parent locals.
Expand Down
2 changes: 1 addition & 1 deletion src/builtins/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2055,7 +2055,7 @@ pub const ModuleBuilder = struct {
) !void {
const funcSigId = try sema.ensureFuncSig(self.compiler, params, ret);
const funcSig = self.compiler.sema.getFuncSig(funcSigId);
if (funcSig.isParamsTyped) {
if (funcSig.reqCallTypeCheck) {
try self.vm.addMethod(typeId, mgId, rt.MethodInit.initHostTyped(funcSigId, ptr, @intCast(params.len)));
} else {
try self.vm.addMethod(typeId, mgId, rt.MethodInit.initHostUntyped(funcSigId, ptr, @intCast(params.len)));
Expand Down
36 changes: 13 additions & 23 deletions src/bytecode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ pub const ByteCodeBuffer = struct {
});
}

pub fn pushFailableDebugSym(self: *ByteCodeBuffer, pc: usize, file: u32, loc: u32, frameLoc: u32, unwindTempIdx: u32) !void {
pub fn pushFailableDebugSym(self: *ByteCodeBuffer, pc: usize, file: u32, loc: u32, frameLoc: u32, unwindTempIdx: u32, localStart: u8, localEnd: u8) !void {
try self.debugTable.append(self.alloc, .{
.pc = @intCast(pc),
.loc = loc,
.file = @intCast(file),
.frameLoc = frameLoc,
.localStart = localStart,
.localEnd = localEnd,
});
try self.debugTempIndexTable.append(self.alloc, unwindTempIdx);
}
Expand Down Expand Up @@ -388,12 +390,6 @@ pub fn dumpInst(pcOffset: u32, code: OpCode, pc: [*]const Inst, len: usize, extr
const dst = pc[3].val;
fmt.printStderr("{} {} left={}, right={}, dst={}", &.{v(pcOffset), v(code), v(left), v(right), v(dst)});
},
.field => {
const recv = pc[1].val;
const dst = pc[2].val;
const symId = @as(*const align(1) u16, @ptrCast(pc + 3)).*;
fmt.printStderr("{} {} recv={}, dst={}, sym={}", &.{v(pcOffset), v(code), v(recv), v(dst), v(symId)});
},
.fieldRetain => {
const recv = pc[1].val;
const dst = pc[2].val;
Expand Down Expand Up @@ -492,11 +488,6 @@ pub fn dumpInst(pcOffset: u32, code: OpCode, pc: [*]const Inst, len: usize, extr
const right = pc[3].val;
fmt.printStderr("{} {} list={}, index={}, right={}", &.{v(pcOffset), v(code), v(list), v(index), v(right)});
},
.init => {
const start = pc[1].val;
const numLocals = pc[2].val;
fmt.printStderr("{} {} start={}, numLocals={}", &.{v(pcOffset), v(code), v(start), v(numLocals) });
},
.coinit => {
const startArgs = pc[1].val;
const numArgs = pc[2].val;
Expand Down Expand Up @@ -609,7 +600,15 @@ pub const DebugSym = extern struct {
frameLoc: u32,

/// CompileChunkId.
file: u32,
file: u16,

/// Which locals are alive before this instruction.
localStart: u8,
localEnd: u8,

pub fn getLocals(sym: cy.DebugSym) cy.IndexSlice(u8) {
return cy.IndexSlice(u8).init(sym.localStart, @intCast(sym.localEnd));
}
};

const DebugMarkerType = enum(u8) {
Expand Down Expand Up @@ -687,7 +686,6 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 {
const numVars = pc[1].val;
return 2 + numVars;
},
.init,
.popTry,
.copy,
.copyRetainSrc,
Expand Down Expand Up @@ -763,8 +761,6 @@ pub fn getInstLenAt(pc: [*]const Inst) u8 {
},
.fieldRetain,
.fieldRetainIC,
.field,
.fieldIC,
.forRangeInit => {
return 8;
},
Expand Down Expand Up @@ -878,8 +874,6 @@ pub const OpCode = enum(u8) {
/// [calleeLocal] [numArgs] [numRet=0/1]
call = vmc.CodeCall,

field = vmc.CodeField,
fieldIC = vmc.CodeFieldIC,
fieldRetain = vmc.CodeFieldRetain,
fieldRetainIC = vmc.CodeFieldRetainIC,
lambda = vmc.CodeLambda,
Expand All @@ -905,10 +899,6 @@ pub const OpCode = enum(u8) {
stringTemplate = vmc.CodeStringTemplate,
negFloat = vmc.CodeNegFloat,

/// Initialize locals starting from `startLocal` to the `none` value.
/// init [startLocal] [numLocals]
init = vmc.CodeInit,

objectTypeCheck = vmc.CodeObjectTypeCheck,
objectSmall = vmc.CodeObjectSmall,
object = vmc.CodeObject,
Expand Down Expand Up @@ -999,7 +989,7 @@ pub const OpCode = enum(u8) {
};

test "bytecode internals." {
try t.eq(std.enums.values(OpCode).len, 109);
try t.eq(std.enums.values(OpCode).len, 106);
try t.eq(@sizeOf(Inst), 1);
try t.eq(@sizeOf(Const), 8);
try t.eq(@alignOf(Const), 8);
Expand Down
Loading

0 comments on commit 0614085

Please sign in to comment.