diff --git a/source/ona/kym/Chunk.zig b/source/ona/kym/Chunk.zig index eb9ff36..4741068 100644 --- a/source/ona/kym/Chunk.zig +++ b/source/ona/kym/Chunk.zig @@ -34,6 +34,12 @@ const Opcode = enum (u8) { sub, mul, div, + + compare_eq, + compare_gt, + compare_lt, + compare_ge, + compare_le, }; const Self = @This(); @@ -144,6 +150,11 @@ pub fn compile_expression(self: *Self, expression: ast.Expression) types.Runtime .subtraction => .sub, .multiplication => .mul, .division => .div, + .equality_comparison => .compare_eq, + .greater_than_comparison => .compare_gt, + .greater_equals_comparison => .compare_ge, + .less_than_comparison => .compare_lt, + .less_equals_comparison => .compare_le, }); }, diff --git a/source/ona/kym/ast.zig b/source/ona/kym/ast.zig index b85094d..e612a6b 100644 --- a/source/ona/kym/ast.zig +++ b/source/ona/kym/ast.zig @@ -8,7 +8,12 @@ pub const BinaryOperation = enum { addition, subtraction, multiplication, - division + division, + equality_comparison, + greater_than_comparison, + greater_equals_comparison, + less_than_comparison, + less_equals_comparison, }; pub const ParsedExpression = union (enum) { @@ -16,11 +21,11 @@ pub const ParsedExpression = union (enum) { invalid: []const u8, pub fn init(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { - var parsed_term_expression = try init_term(allocator, tokenizer); + var parsed_lhs_expression = try init_equality(allocator, tokenizer); - switch (parsed_term_expression) { - .valid => |*term_expression| { - var expression = term_expression.*; + switch (parsed_lhs_expression) { + .valid => |*lhs_expression| { + var expression = lhs_expression.*; var is_invalid = true; defer if (is_invalid) { @@ -32,12 +37,21 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected right-hand side of expression after `+`"}; } - var parsed_binary_expression = try init_binary(allocator, tokenizer, &expression, .addition); + var parsed_rhs_expression = try init_equality(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .addition, + lhs_expression, + rhs_expression); + }, - expression = switch (parsed_binary_expression) { - .valid => |binary_expression| binary_expression, .invalid => |details| return ParsedExpression{.invalid = details}, - }; + } } if (tokenizer.current_token == .symbol_minus) { @@ -45,12 +59,21 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected right-hand side of expression after `-`"}; } - var parsed_binary_expression = try init_binary(allocator, tokenizer, &expression, .subtraction); + var parsed_rhs_expression = try init_equality(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .subtraction, + lhs_expression, + rhs_expression); + }, - expression = switch (parsed_binary_expression) { - .valid => |binary_expression| binary_expression, .invalid => |details| return ParsedExpression{.invalid = details}, - }; + } } is_invalid = false; @@ -58,41 +81,172 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.valid = expression}; }, - .invalid => |details| { - return ParsedExpression{.invalid = details}; - }, + .invalid => |details| return ParsedExpression{.invalid = details}, } } - fn init_binary(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer, lhs_expression: *const Expression, operation: BinaryOperation) coral.io.AllocationError!ParsedExpression { - var parsed_expression = try init_term(allocator, tokenizer); + pub fn init_comparison( + allocator: coral.io.Allocator, + tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { - switch (parsed_expression) { - .valid => |*expression| { - errdefer expression.deinit(allocator); + var parsed_lhs_expression = try init_term(allocator, tokenizer); - const rhs_expression = try coral.io.allocate_one(allocator, expression.*); + switch (parsed_lhs_expression) { + .valid => |*lhs_expression| { + var expression = lhs_expression.*; + var is_invalid = true; - errdefer coral.io.deallocate(allocator, rhs_expression); - - return ParsedExpression{ - .valid = .{ - .binary_operation = .{ - .kind = operation, - .lhs_expression = try coral.io.allocate_one(allocator, lhs_expression.*), - .rhs_expression = rhs_expression, - }, - }, + defer if (is_invalid) { + expression.deinit(allocator); }; + + if (tokenizer.current_token == .symbol_greater_than) { + if (!tokenizer.step()) { + return ParsedExpression{.invalid = "expected right-hand side of expression after `>`"}; + } + + var parsed_rhs_expression = try init_term(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .greater_than_comparison, + lhs_expression, + rhs_expression); + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + if (tokenizer.current_token == .symbol_greater_equals) { + if (!tokenizer.step()) { + return ParsedExpression{.invalid = "expected right-hand side of expression after `>=`"}; + } + + var parsed_rhs_expression = try init_term(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .greater_equals_comparison, + lhs_expression, + rhs_expression); + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + if (tokenizer.current_token == .symbol_less_than) { + if (!tokenizer.step()) { + return ParsedExpression{.invalid = "expected right-hand side of expression after `<`"}; + } + + var parsed_rhs_expression = try init_term(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .less_than_comparison, + lhs_expression, + rhs_expression); + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + if (tokenizer.current_token == .symbol_less_equals) { + if (!tokenizer.step()) { + return ParsedExpression{.invalid = "expected right-hand side of expression after `<=`"}; + } + + var parsed_rhs_expression = try init_term(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .less_equals_comparison, + lhs_expression, + rhs_expression); + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + is_invalid = false; + + return ParsedExpression{.valid = expression}; }, - .invalid => |details| { - return ParsedExpression{.invalid = details}; - }, + .invalid => |details| return ParsedExpression{.invalid = details}, } } - fn init_factor(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { + fn init_equality( + allocator: coral.io.Allocator, + tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { + + var parsed_lhs_expression = try init_comparison(allocator, tokenizer); + + switch (parsed_lhs_expression) { + .valid => |*lhs_expression| { + var expression = lhs_expression.*; + var is_invalid = true; + + defer if (is_invalid) { + expression.deinit(allocator); + }; + + if (tokenizer.current_token == .symbol_double_equals) { + if (!tokenizer.step()) { + return ParsedExpression{.invalid = "expected right-hand side of expression after `==`"}; + } + + var parsed_rhs_expression = try init_comparison(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .equality_comparison, + lhs_expression, + rhs_expression); + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + is_invalid = false; + + return ParsedExpression{.valid = expression}; + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } + } + + fn init_factor( + allocator: coral.io.Allocator, + tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { + switch (tokenizer.current_token) { .symbol_paren_left => { if (!tokenizer.step()) { @@ -120,9 +274,7 @@ pub const ParsedExpression = union (enum) { }; }, - .invalid => |details| { - return ParsedExpression{.invalid = details}; - } + .invalid => |details| return ParsedExpression{.invalid = details}, } }, @@ -180,7 +332,22 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected expression after numeric negation (`-`)"}; } - return try init_unary(allocator, tokenizer, .numeric_negation); + var parsed_factor_expression = try init_factor(allocator, tokenizer); + + switch (parsed_factor_expression) { + .valid => |*factor_expression| { + errdefer factor_expression.deinit(allocator); + + return ParsedExpression{ + .valid = try Expression.init_unary_operation( + allocator, + .numeric_negation, + factor_expression), + }; + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } }, .symbol_bang => { @@ -188,19 +355,34 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected expression after boolean negation (`!`)"}; } - return try init_unary(allocator, tokenizer, .boolean_negation); + var parsed_factor_expression = try init_factor(allocator, tokenizer); + + switch (parsed_factor_expression) { + .valid => |*factor_expression| { + errdefer factor_expression.deinit(allocator); + + return ParsedExpression{ + .valid = try Expression.init_unary_operation( + allocator, + .boolean_negation, + factor_expression), + }; + }, + + .invalid => |details| return ParsedExpression{.invalid = details}, + } }, else => return ParsedExpression{.invalid = "unexpected token in expression"}, } } - fn init_term(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { - var parsed_factor_expression = try init_factor(allocator, tokenizer); + pub fn init_term(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { + var parsed_lhs_expression = try init_factor(allocator, tokenizer); - switch (parsed_factor_expression) { - .valid => |*factor_expression| { - var expression = factor_expression.*; + switch (parsed_lhs_expression) { + .valid => |*lhs_expression| { + var expression = lhs_expression.*; var is_invalid = true; defer if (is_invalid) { @@ -212,12 +394,21 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected right-hand side of expression after `*`"}; } - var parsed_binary_expression = try init_binary(allocator, tokenizer, &expression, .multiplication); + var parsed_rhs_expression = try init_factor(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .multiplication, + lhs_expression, + rhs_expression); + }, - expression = switch (parsed_binary_expression) { - .valid => |binary_expression| binary_expression, .invalid => |details| return ParsedExpression{.invalid = details}, - }; + } } if (tokenizer.current_token == .symbol_forward_slash) { @@ -225,12 +416,21 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.invalid = "expected right-hand side of expression after `/`"}; } - var parsed_binary_expression = try init_binary(allocator, tokenizer, &expression, .division); + var parsed_rhs_expression = try init_equality(allocator, tokenizer); + + switch (parsed_rhs_expression) { + .valid => |*rhs_expression| { + errdefer rhs_expression.deinit(allocator); + + expression = try Expression.init_binary_operation( + allocator, + .division, + lhs_expression, + rhs_expression); + }, - expression = switch (parsed_binary_expression) { - .valid => |binary_expression| binary_expression, .invalid => |details| return ParsedExpression{.invalid = details}, - }; + } } is_invalid = false; @@ -238,32 +438,7 @@ pub const ParsedExpression = union (enum) { return ParsedExpression{.valid = expression}; }, - .invalid => |details| { - return ParsedExpression{.invalid = details}; - }, - } - } - - fn init_unary(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer, operation: UnaryOperation) coral.io.AllocationError!ParsedExpression { - var parsed_factor_expression = try init_factor(allocator, tokenizer); - - switch (parsed_factor_expression) { - .valid => |*factor_expression| { - errdefer factor_expression.deinit(allocator); - - return ParsedExpression{ - .valid = .{ - .unary_operation = .{ - .kind = operation, - .expression = try coral.io.allocate_one(allocator, factor_expression.*), - }, - }, - }; - }, - - .invalid => |details| { - return ParsedExpression{.invalid = details}; - }, + .invalid => |details| return ParsedExpression{.invalid = details}, } } }; @@ -393,6 +568,46 @@ pub const Expression = union (enum) { } } } + + fn init_binary_operation( + allocator: coral.io.Allocator, + kind: BinaryOperation, + lhs_expression: *const Expression, + rhs_operation: *const Expression) coral.io.AllocationError!Expression { + + const allocated_lhs_expression = try coral.io.allocate_one(allocator, lhs_expression.*); + + errdefer coral.io.deallocate(allocator, allocated_lhs_expression); + + const allocated_rhs_expression = try coral.io.allocate_one(allocator, rhs_operation.*); + + errdefer coral.io.deallocate(allocator, allocated_rhs_expression); + + return Expression{ + .binary_operation = .{ + .kind = kind, + .lhs_expression = allocated_lhs_expression, + .rhs_expression = allocated_rhs_expression, + }, + }; + } + + fn init_unary_operation( + allocator: coral.io.Allocator, + kind: UnaryOperation, + expression: *const Expression) coral.io.AllocationError!Expression { + + const allocated_expression = try coral.io.allocate_one(allocator, expression.*); + + errdefer coral.io.deallocate(allocator, allocated_expression); + + return Expression{ + .unary_operation = .{ + .kind = kind, + .expression = allocated_expression, + }, + }; + } }; pub const Statements = struct { diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index 634fe9b..7baa988 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -22,7 +22,13 @@ pub const Token = union(enum) { symbol_bracket_left, symbol_bracket_right, symbol_period, - symbol_arrow, + symbol_lambda, + symbol_less_than, + symbol_less_equals, + symbol_greater_than, + symbol_greater_equals, + symbol_equals, + symbol_double_equals, integer: []const u8, real: []const u8, @@ -311,9 +317,60 @@ pub const Tokenizer = struct { }, '=' => { - self.current_token = .symbol_assign; cursor += 1; + if (self.has_next()) { + switch (self.source[cursor]) { + '=' => { + cursor += 1; + self.current_token = .symbol_double_equals; + + return true; + }, + + '>' => { + cursor += 1; + self.current_token = .symbol_lambda; + + return true; + }, + + else => {}, + } + } + + self.current_token = .symbol_equals; + + return true; + }, + + '<' => { + cursor += 1; + + if (self.has_next() and (self.source[cursor] == '=')) { + cursor += 1; + self.current_token = .symbol_less_equals; + + return true; + } + + self.current_token = .symbol_less_than; + + return true; + }, + + '>' => { + cursor += 1; + + if (self.has_next() and (self.source[cursor] == '=')) { + cursor += 1; + self.current_token = .symbol_greater_equals; + + return true; + } + + self.current_token = .symbol_greater_than; + return true; }, diff --git a/source/ona/kym/types.zig b/source/ona/kym/types.zig index 5c2a9a8..e9c56df 100644 --- a/source/ona/kym/types.zig +++ b/source/ona/kym/types.zig @@ -38,38 +38,6 @@ pub const RuntimeError = coral.io.AllocationError || CheckError || error { BadSyntax, }; -pub fn SmallStack(comptime Element: type, comptime default: Element) type { - const maximum = 255; - - return struct { - buffer: [maximum]Element = [_]Element{default} ** maximum, - count: u8 = 0, - - const Self = @This(); - - fn peek(self: Self) ?Element { - if (self.count == 0) return null; - - return self.buffer[self.count - 1]; - } - - fn pop(self: *Self) ?Element { - if (self.count == 0) return null; - - self.count -= 1; - - return self.buffer[self.count]; - } - - fn push(self: *Self, element: Element) !void { - if (self.count == maximum) return error.OutOfMemory; - - self.buffer[self.count] = element; - self.count += 1; - } - }; -} - pub const Val = union (Primitive) { nil, false,