Add support for binary comparison to Kym
This commit is contained in:
parent
fe8e98e753
commit
49ace97ee3
|
@ -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,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -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};
|
||||
},
|
||||
.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) {
|
||||
.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 {
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue