Add support for binary comparison to Kym

This commit is contained in:
kayomn 2023-05-28 02:32:00 +00:00
parent fe8e98e753
commit 49ace97ee3
4 changed files with 365 additions and 114 deletions

View File

@ -34,6 +34,12 @@ const Opcode = enum (u8) {
sub, sub,
mul, mul,
div, div,
compare_eq,
compare_gt,
compare_lt,
compare_ge,
compare_le,
}; };
const Self = @This(); const Self = @This();
@ -144,6 +150,11 @@ pub fn compile_expression(self: *Self, expression: ast.Expression) types.Runtime
.subtraction => .sub, .subtraction => .sub,
.multiplication => .mul, .multiplication => .mul,
.division => .div, .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,
}); });
}, },

View File

@ -8,7 +8,12 @@ pub const BinaryOperation = enum {
addition, addition,
subtraction, subtraction,
multiplication, multiplication,
division division,
equality_comparison,
greater_than_comparison,
greater_equals_comparison,
less_than_comparison,
less_equals_comparison,
}; };
pub const ParsedExpression = union (enum) { pub const ParsedExpression = union (enum) {
@ -16,11 +21,11 @@ pub const ParsedExpression = union (enum) {
invalid: []const u8, invalid: []const u8,
pub fn init(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { 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) { switch (parsed_lhs_expression) {
.valid => |*term_expression| { .valid => |*lhs_expression| {
var expression = term_expression.*; var expression = lhs_expression.*;
var is_invalid = true; var is_invalid = true;
defer if (is_invalid) { defer if (is_invalid) {
@ -32,12 +37,21 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.invalid = "expected right-hand side of expression after `+`"}; 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}, .invalid => |details| return ParsedExpression{.invalid = details},
}; }
} }
if (tokenizer.current_token == .symbol_minus) { 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 `-`"}; 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}, .invalid => |details| return ParsedExpression{.invalid = details},
}; }
} }
is_invalid = false; is_invalid = false;
@ -58,41 +81,172 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.valid = expression}; return ParsedExpression{.valid = expression};
}, },
.invalid => |details| { .invalid => |details| return ParsedExpression{.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 { pub fn init_comparison(
var parsed_expression = try init_term(allocator, tokenizer); allocator: coral.io.Allocator,
tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression {
switch (parsed_expression) { var parsed_lhs_expression = try init_term(allocator, tokenizer);
.valid => |*expression| {
errdefer expression.deinit(allocator);
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); defer if (is_invalid) {
expression.deinit(allocator);
return ParsedExpression{
.valid = .{
.binary_operation = .{
.kind = operation,
.lhs_expression = try coral.io.allocate_one(allocator, lhs_expression.*),
.rhs_expression = rhs_expression,
},
},
}; };
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| { .invalid => |details| return ParsedExpression{.invalid = details},
return ParsedExpression{.invalid = details};
},
} }
} }
fn init_factor(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { 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},
}
}
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) { switch (tokenizer.current_token) {
.symbol_paren_left => { .symbol_paren_left => {
if (!tokenizer.step()) { if (!tokenizer.step()) {
@ -120,9 +274,7 @@ pub const ParsedExpression = union (enum) {
}; };
}, },
.invalid => |details| { .invalid => |details| return ParsedExpression{.invalid = details},
return ParsedExpression{.invalid = details};
}
} }
}, },
@ -180,7 +332,22 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.invalid = "expected expression after numeric negation (`-`)"}; 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 => { .symbol_bang => {
@ -188,19 +355,34 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.invalid = "expected expression after boolean negation (`!`)"}; 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"}, else => return ParsedExpression{.invalid = "unexpected token in expression"},
} }
} }
fn init_term(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression { pub fn init_term(allocator: coral.io.Allocator, tokenizer: *tokens.Tokenizer) coral.io.AllocationError!ParsedExpression {
var parsed_factor_expression = try init_factor(allocator, tokenizer); var parsed_lhs_expression = try init_factor(allocator, tokenizer);
switch (parsed_factor_expression) { switch (parsed_lhs_expression) {
.valid => |*factor_expression| { .valid => |*lhs_expression| {
var expression = factor_expression.*; var expression = lhs_expression.*;
var is_invalid = true; var is_invalid = true;
defer if (is_invalid) { defer if (is_invalid) {
@ -212,12 +394,21 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.invalid = "expected right-hand side of expression after `*`"}; 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}, .invalid => |details| return ParsedExpression{.invalid = details},
}; }
} }
if (tokenizer.current_token == .symbol_forward_slash) { 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 `/`"}; 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}, .invalid => |details| return ParsedExpression{.invalid = details},
}; }
} }
is_invalid = false; is_invalid = false;
@ -238,32 +438,7 @@ pub const ParsedExpression = union (enum) {
return ParsedExpression{.valid = expression}; return ParsedExpression{.valid = expression};
}, },
.invalid => |details| { .invalid => |details| return ParsedExpression{.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};
},
} }
} }
}; };
@ -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 { pub const Statements = struct {

View File

@ -22,7 +22,13 @@ pub const Token = union(enum) {
symbol_bracket_left, symbol_bracket_left,
symbol_bracket_right, symbol_bracket_right,
symbol_period, 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, integer: []const u8,
real: []const u8, real: []const u8,
@ -311,9 +317,60 @@ pub const Tokenizer = struct {
}, },
'=' => { '=' => {
self.current_token = .symbol_assign;
cursor += 1; 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; return true;
}, },

View File

@ -38,38 +38,6 @@ pub const RuntimeError = coral.io.AllocationError || CheckError || error {
BadSyntax, 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) { pub const Val = union (Primitive) {
nil, nil,
false, false,