implementing string indexes
This commit is contained in:
parent
b839d67cea
commit
0bc5f495b9
3
samples/indices.lox
Normal file
3
samples/indices.lox
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
var alphabet = "abcdefghijklmnopqrstuvwxyz"; var X = alphabet; print "hello " + X[3] + X[0] + X[10];
|
||||||
|
|
||||||
|
var s = " "; s[0] = "d"; s[1] = "a"; s[2] = "k"; var hey = "hello " + s + "!"; print hey;
|
@ -152,6 +152,8 @@ pub const Chunk = struct {
|
|||||||
@intFromEnum(OpCode.OP_GET_PROPERTY) => return utils.constant_instruction("OP_GET_PROPERTY", self, offset),
|
@intFromEnum(OpCode.OP_GET_PROPERTY) => return utils.constant_instruction("OP_GET_PROPERTY", self, offset),
|
||||||
@intFromEnum(OpCode.OP_SET_PROPERTY) => return utils.constant_instruction("OP_SET_PROPERTY", self, offset),
|
@intFromEnum(OpCode.OP_SET_PROPERTY) => return utils.constant_instruction("OP_SET_PROPERTY", self, offset),
|
||||||
@intFromEnum(OpCode.OP_METHOD) => return utils.constant_instruction("OP_METHOD", self, offset),
|
@intFromEnum(OpCode.OP_METHOD) => return utils.constant_instruction("OP_METHOD", self, offset),
|
||||||
|
@intFromEnum(OpCode.OP_INDEX_GET) => return utils.simple_instruction("OP_INDEX_GET", offset),
|
||||||
|
@intFromEnum(OpCode.OP_INDEX_SET) => return utils.simple_instruction("OP_INDEX_SET", offset),
|
||||||
else => {
|
else => {
|
||||||
debug.print("unknown opcode {d}\n", .{instruction});
|
debug.print("unknown opcode {d}\n", .{instruction});
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
@ -29,6 +29,7 @@ const Precedence = enum {
|
|||||||
Term,
|
Term,
|
||||||
Factor,
|
Factor,
|
||||||
Unary,
|
Unary,
|
||||||
|
Index,
|
||||||
Call,
|
Call,
|
||||||
Primary,
|
Primary,
|
||||||
};
|
};
|
||||||
@ -206,6 +207,18 @@ pub const Parser = struct {
|
|||||||
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
|
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn index_(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
try self.expression();
|
||||||
|
self.consume(TokenType.RIGHT_BRACKET, "Expecting ']");
|
||||||
|
|
||||||
|
if (can_assign and self.match(TokenType.EQUAL)) {
|
||||||
|
try self.expression();
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_INDEX_SET));
|
||||||
|
} else {
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_INDEX_GET));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn unary(self: *Parser, can_assign: bool) ParsingError!void {
|
fn unary(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
_ = can_assign;
|
_ = can_assign;
|
||||||
|
|
||||||
@ -267,6 +280,8 @@ pub const Parser = struct {
|
|||||||
TokenType.DOT => ParserRule{ .prefix = null, .infix = dot, .precedence = Precedence.Call },
|
TokenType.DOT => ParserRule{ .prefix = null, .infix = dot, .precedence = Precedence.Call },
|
||||||
TokenType.MINUS => ParserRule{ .prefix = unary, .infix = binary, .precedence = Precedence.Term },
|
TokenType.MINUS => ParserRule{ .prefix = unary, .infix = binary, .precedence = Precedence.Term },
|
||||||
TokenType.PLUS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Term },
|
TokenType.PLUS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Term },
|
||||||
|
TokenType.LEFT_BRACKET => ParserRule{ .prefix = null, .infix = index_, .precedence = Precedence.Unary },
|
||||||
|
TokenType.RIGHT_BRACKET => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SEMICOLON => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SEMICOLON => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SLASH => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
TokenType.SLASH => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
||||||
TokenType.STAR => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
TokenType.STAR => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
||||||
@ -369,6 +384,17 @@ pub const Parser = struct {
|
|||||||
set_op = OpCode.OP_SET_GLOBAL;
|
set_op = OpCode.OP_SET_GLOBAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle assignments by index:
|
||||||
|
// - push value to the stack
|
||||||
|
// - modify it (through a OP_INDEX_SET)
|
||||||
|
// - update the variable
|
||||||
|
if (can_assign and self.match(TokenType.LEFT_BRACKET)) {
|
||||||
|
try self.emit_bytes(@intFromEnum(get_op), @intCast(arg));
|
||||||
|
try self.index_(can_assign);
|
||||||
|
try self.emit_bytes(@intFromEnum(set_op), @intCast(arg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (can_assign and self.match(TokenType.EQUAL)) {
|
if (can_assign and self.match(TokenType.EQUAL)) {
|
||||||
try self.expression();
|
try self.expression();
|
||||||
try self.emit_bytes(@intFromEnum(set_op), @intCast(arg));
|
try self.emit_bytes(@intFromEnum(set_op), @intCast(arg));
|
||||||
|
@ -32,4 +32,6 @@ pub const OpCode = enum(u8) {
|
|||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
OP_CLASS,
|
OP_CLASS,
|
||||||
OP_METHOD,
|
OP_METHOD,
|
||||||
|
OP_INDEX_SET,
|
||||||
|
OP_INDEX_GET,
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,8 @@ pub const TokenType = enum {
|
|||||||
RIGHT_PAREN,
|
RIGHT_PAREN,
|
||||||
LEFT_BRACE,
|
LEFT_BRACE,
|
||||||
RIGHT_BRACE,
|
RIGHT_BRACE,
|
||||||
|
LEFT_BRACKET,
|
||||||
|
RIGHT_BRACKET,
|
||||||
COMMA,
|
COMMA,
|
||||||
DOT,
|
DOT,
|
||||||
MINUS,
|
MINUS,
|
||||||
@ -55,6 +57,8 @@ pub const TokenType = enum {
|
|||||||
TokenType.RIGHT_PAREN => "RIGHT_PAREN",
|
TokenType.RIGHT_PAREN => "RIGHT_PAREN",
|
||||||
TokenType.LEFT_BRACE => "LEFT_BRACE",
|
TokenType.LEFT_BRACE => "LEFT_BRACE",
|
||||||
TokenType.RIGHT_BRACE => "RIGHT_BRACE",
|
TokenType.RIGHT_BRACE => "RIGHT_BRACE",
|
||||||
|
TokenType.LEFT_BRACKET => "LEFT_BRACKET",
|
||||||
|
TokenType.RIGHT_BRACKET => "RIGHT_BRACKET",
|
||||||
TokenType.COMMA => "COMMA",
|
TokenType.COMMA => "COMMA",
|
||||||
TokenType.DOT => "DOT",
|
TokenType.DOT => "DOT",
|
||||||
TokenType.MINUS => "MINUS",
|
TokenType.MINUS => "MINUS",
|
||||||
@ -140,6 +144,8 @@ pub const Scanner = struct {
|
|||||||
')' => self.make_token(TokenType.RIGHT_PAREN),
|
')' => self.make_token(TokenType.RIGHT_PAREN),
|
||||||
'{' => self.make_token(TokenType.LEFT_BRACE),
|
'{' => self.make_token(TokenType.LEFT_BRACE),
|
||||||
'}' => self.make_token(TokenType.RIGHT_BRACE),
|
'}' => self.make_token(TokenType.RIGHT_BRACE),
|
||||||
|
'[' => self.make_token(TokenType.LEFT_BRACKET),
|
||||||
|
']' => self.make_token(TokenType.RIGHT_BRACKET),
|
||||||
';' => self.make_token(TokenType.SEMICOLON),
|
';' => self.make_token(TokenType.SEMICOLON),
|
||||||
',' => self.make_token(TokenType.COMMA),
|
',' => self.make_token(TokenType.COMMA),
|
||||||
'.' => self.make_token(TokenType.DOT),
|
'.' => self.make_token(TokenType.DOT),
|
||||||
|
34
src/vm.zig
34
src/vm.zig
@ -338,6 +338,40 @@ pub const VM = struct {
|
|||||||
@intFromEnum(OpCode.OP_METHOD) => {
|
@intFromEnum(OpCode.OP_METHOD) => {
|
||||||
self.define_method(self.read_constant().as_string());
|
self.define_method(self.read_constant().as_string());
|
||||||
},
|
},
|
||||||
|
@intFromEnum(OpCode.OP_INDEX_GET) => {
|
||||||
|
if (!self.peek(0).is_number() or !self.peek(1).is_string()) {
|
||||||
|
self.runtime_error("A number and a string are required for indexes.");
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
const index_val = self.pop();
|
||||||
|
const value = self.pop();
|
||||||
|
|
||||||
|
const index: usize = @as(usize, @intFromFloat(index_val.as_number()));
|
||||||
|
if (index >= value.as_cstring().len) {
|
||||||
|
self.runtime_error("The index must be set between 0 and string len.");
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
const c = value.as_cstring()[index .. index + 1];
|
||||||
|
|
||||||
|
_ = try self.push(Value.obj_val(&self.copy_string(c).obj));
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_INDEX_SET) => {
|
||||||
|
const value = self.pop();
|
||||||
|
const index_val = self.pop();
|
||||||
|
const origin = self.pop();
|
||||||
|
|
||||||
|
if (!value.is_string() or value.as_cstring().len != 1) {
|
||||||
|
self.runtime_error("Value to assign must be one byte.");
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index: usize = @as(usize, @intFromFloat(index_val.as_number()));
|
||||||
|
|
||||||
|
var str = self.allocator.dupe(u8, origin.as_cstring()) catch unreachable;
|
||||||
|
str[index] = value.as_cstring()[0];
|
||||||
|
|
||||||
|
_ = try self.push(Value.obj_val(&self.take_string(str).obj));
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
debug.print("Invalid instruction: {d}\n", .{instruction});
|
debug.print("Invalid instruction: {d}\n", .{instruction});
|
||||||
return InterpretResult.RUNTIME_ERROR;
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
Loading…
Reference in New Issue
Block a user