implementing this
This commit is contained in:
parent
3c1c37799c
commit
b522f05c8c
5
samples/ch28_this_outside.lox
Normal file
5
samples/ch28_this_outside.lox
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
print this;
|
||||||
|
|
||||||
|
fun invalid() {
|
||||||
|
print this;
|
||||||
|
}
|
@ -35,6 +35,10 @@ const Precedence = enum {
|
|||||||
|
|
||||||
const ParserFn = *const fn (*Parser, bool) ParsingError!void;
|
const ParserFn = *const fn (*Parser, bool) ParsingError!void;
|
||||||
|
|
||||||
|
const ClassCompiler = struct {
|
||||||
|
enclosing: ?*ClassCompiler,
|
||||||
|
};
|
||||||
|
|
||||||
const ParserRule = struct {
|
const ParserRule = struct {
|
||||||
prefix: ?ParserFn,
|
prefix: ?ParserFn,
|
||||||
infix: ?ParserFn,
|
infix: ?ParserFn,
|
||||||
@ -49,6 +53,7 @@ pub const Parser = struct {
|
|||||||
had_error: bool,
|
had_error: bool,
|
||||||
panic_mode: bool,
|
panic_mode: bool,
|
||||||
vm: *VM,
|
vm: *VM,
|
||||||
|
class_compiler: ?*ClassCompiler,
|
||||||
|
|
||||||
fn new(vm: *VM, compiler: *Compiler, scanner: *Scanner) Parser {
|
fn new(vm: *VM, compiler: *Compiler, scanner: *Scanner) Parser {
|
||||||
return Parser{
|
return Parser{
|
||||||
@ -59,6 +64,7 @@ pub const Parser = struct {
|
|||||||
.had_error = false,
|
.had_error = false,
|
||||||
.panic_mode = false,
|
.panic_mode = false,
|
||||||
.vm = vm,
|
.vm = vm,
|
||||||
|
.class_compiler = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +72,10 @@ pub const Parser = struct {
|
|||||||
return self.compiler.function.chunk;
|
return self.compiler.function.chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fn current_class_compiler(self: *Parser) ?*ClassCompiler {
|
||||||
|
return self.class_compiler;
|
||||||
|
}
|
||||||
|
|
||||||
fn advance(self: *Parser) void {
|
fn advance(self: *Parser) void {
|
||||||
self.previous = self.current;
|
self.previous = self.current;
|
||||||
|
|
||||||
@ -278,7 +288,7 @@ pub const Parser = struct {
|
|||||||
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.THIS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.THIS => ParserRule{ .prefix = this_, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.WHILE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.WHILE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
@ -871,6 +881,11 @@ pub const Parser = struct {
|
|||||||
try self.emit_bytes(@intFromEnum(OpCode.OP_CLASS), name_constant);
|
try self.emit_bytes(@intFromEnum(OpCode.OP_CLASS), name_constant);
|
||||||
try self.define_variable(name_constant);
|
try self.define_variable(name_constant);
|
||||||
|
|
||||||
|
var class_compiler = ClassCompiler{
|
||||||
|
.enclosing = self.current_class_compiler(),
|
||||||
|
};
|
||||||
|
self.class_compiler = &class_compiler;
|
||||||
|
|
||||||
try self.named_variable(class_name, false);
|
try self.named_variable(class_name, false);
|
||||||
|
|
||||||
self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
|
self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
|
||||||
@ -879,6 +894,8 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
|
self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
|
||||||
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
|
||||||
|
self.class_compiler = self.current_class_compiler().?.enclosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dot(self: *Parser, can_assign: bool) ParsingError!void {
|
fn dot(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
@ -897,14 +914,25 @@ pub const Parser = struct {
|
|||||||
self.consume(TokenType.IDENTIFIER, "Expect method name.");
|
self.consume(TokenType.IDENTIFIER, "Expect method name.");
|
||||||
const constant = try self.identifier_constant(self.previous.?);
|
const constant = try self.identifier_constant(self.previous.?);
|
||||||
|
|
||||||
try self.function(FunctionType.Function);
|
try self.function(FunctionType.Method);
|
||||||
try self.emit_bytes(@intFromEnum(OpCode.OP_METHOD), constant);
|
try self.emit_bytes(@intFromEnum(OpCode.OP_METHOD), constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn this_(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
if (self.current_class_compiler() == null) {
|
||||||
|
self.error_msg("Can't use 'this' outside of a class.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = can_assign;
|
||||||
|
try self.variable(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const FunctionType = enum {
|
const FunctionType = enum {
|
||||||
Function,
|
Function,
|
||||||
Script,
|
Script,
|
||||||
|
Method,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Compiler = struct {
|
pub const Compiler = struct {
|
||||||
@ -931,6 +959,8 @@ pub const Compiler = struct {
|
|||||||
.enclosing = enclosing,
|
.enclosing = enclosing,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
compiler.local_count += 1;
|
||||||
|
|
||||||
compiler.locals[0].depth = 0;
|
compiler.locals[0].depth = 0;
|
||||||
compiler.locals[0].name = Token{
|
compiler.locals[0].name = Token{
|
||||||
.token_type = TokenType.EOF,
|
.token_type = TokenType.EOF,
|
||||||
@ -940,7 +970,10 @@ pub const Compiler = struct {
|
|||||||
};
|
};
|
||||||
compiler.locals[0].is_captured = false;
|
compiler.locals[0].is_captured = false;
|
||||||
|
|
||||||
compiler.local_count += 1;
|
if (function_type != FunctionType.Function) {
|
||||||
|
compiler.locals[0].name.start = "this";
|
||||||
|
compiler.locals[0].name.length = 4;
|
||||||
|
}
|
||||||
|
|
||||||
return compiler;
|
return compiler;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +504,7 @@ pub const VM = struct {
|
|||||||
},
|
},
|
||||||
ObjType.BoundMethod => {
|
ObjType.BoundMethod => {
|
||||||
const bound_method = callee.as_obj().as_bound_method();
|
const bound_method = callee.as_obj().as_bound_method();
|
||||||
|
self.stack[self.stack_top - arg_count - 1] = bound_method.receiver;
|
||||||
return self.call(bound_method.method, arg_count);
|
return self.call(bound_method.method, arg_count);
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
|
Loading…
Reference in New Issue
Block a user