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,
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,
});
},

View File

@ -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 {

View File

@ -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;
},

View File

@ -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,