Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
|
|
@ -1,343 +1,179 @@
|
|||
const std = @import("std");
|
||||
const print = std.debug.print;
|
||||
const stdin = std.io.getStdIn().reader();
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const StrTok = struct {
|
||||
s: []const u8,
|
||||
len: usize,
|
||||
prec: i32,
|
||||
assoc: i32,
|
||||
const Number = f64;
|
||||
|
||||
const Operator = struct {
|
||||
token: u8,
|
||||
operation: *const fn (Number, Number) Number,
|
||||
precedence: u8,
|
||||
is_left_associative: bool,
|
||||
|
||||
fn apply(self: Operator, x: Number, y: Number) Number {
|
||||
return self.operation(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
const Pattern = struct {
|
||||
str: []const u8,
|
||||
assoc: i32 = 0,
|
||||
prec: i32 = 0,
|
||||
const Token = union(enum) {
|
||||
digit: Number,
|
||||
operator: Operator,
|
||||
left_paren,
|
||||
right_paren,
|
||||
};
|
||||
|
||||
const Assoc = struct {
|
||||
pub const NONE: i32 = 0;
|
||||
pub const L: i32 = 1;
|
||||
pub const R: i32 = 2;
|
||||
};
|
||||
fn opAdd(x: Number, y: Number) Number { return x + y; }
|
||||
fn opSub(x: Number, y: Number) Number { return x - y; }
|
||||
fn opMul(x: Number, y: Number) Number { return x * y; }
|
||||
fn opDiv(x: Number, y: Number) Number { return x / y; }
|
||||
fn opPow(x: Number, y: Number) Number { return std.math.pow(Number, x, y); }
|
||||
|
||||
const pat_eos = Pattern{ .str = "" };
|
||||
fn makeOperator(
|
||||
token: u8,
|
||||
precedence: u8,
|
||||
is_left_associative: bool,
|
||||
operation: *const fn (Number, Number) Number,
|
||||
) Token {
|
||||
return .{ .operator = .{
|
||||
.token = token,
|
||||
.operation = operation,
|
||||
.precedence = precedence,
|
||||
.is_left_associative = is_left_associative,
|
||||
} };
|
||||
}
|
||||
|
||||
const pat_ops = [_]Pattern{
|
||||
Pattern{ .str = ")", .assoc = Assoc.NONE, .prec = -1 },
|
||||
Pattern{ .str = "**", .assoc = Assoc.R, .prec = 3 },
|
||||
Pattern{ .str = "^", .assoc = Assoc.R, .prec = 3 },
|
||||
Pattern{ .str = "*", .assoc = Assoc.L, .prec = 2 },
|
||||
Pattern{ .str = "/", .assoc = Assoc.L, .prec = 2 },
|
||||
Pattern{ .str = "+", .assoc = Assoc.L, .prec = 1 },
|
||||
Pattern{ .str = "-", .assoc = Assoc.L, .prec = 1 },
|
||||
Pattern{ .str = "" },
|
||||
};
|
||||
fn lexToken(c: u8) error{InvalidCharacter}!Token {
|
||||
return switch (c) {
|
||||
'0'...'9' => .{ .digit = @floatFromInt(c - '0') },
|
||||
'+' => makeOperator('+', 1, true, &opAdd),
|
||||
'-' => makeOperator('-', 1, true, &opSub),
|
||||
'*' => makeOperator('*', 2, true, &opMul),
|
||||
'/' => makeOperator('/', 2, true, &opDiv),
|
||||
'^' => makeOperator('^', 3, false, &opPow),
|
||||
'(' => .left_paren,
|
||||
')' => .right_paren,
|
||||
else => error.InvalidCharacter,
|
||||
};
|
||||
}
|
||||
|
||||
const pat_arg = [_]Pattern{
|
||||
Pattern{ .str = "number" }, // This will be matched differently
|
||||
Pattern{ .str = "identifier" }, // This will be matched differently
|
||||
Pattern{ .str = "(", .assoc = Assoc.L, .prec = -1 },
|
||||
Pattern{ .str = "" },
|
||||
};
|
||||
|
||||
const MAX_STACK = 128;
|
||||
const MAX_QUEUE = 128;
|
||||
|
||||
var stack: [MAX_STACK]StrTok = undefined;
|
||||
var queue: [MAX_QUEUE]StrTok = undefined;
|
||||
var l_queue: usize = 0;
|
||||
var l_stack: usize = 0;
|
||||
var prec_booster: i32 = 0;
|
||||
|
||||
fn qpush(tok: StrTok) !void {
|
||||
if (l_queue >= MAX_QUEUE) {
|
||||
return error.QueueOverflow;
|
||||
fn lex(allocator: Allocator, input: []const u8) !std.ArrayList(Token) {
|
||||
var tokens = std.ArrayList(Token).init(allocator);
|
||||
errdefer tokens.deinit();
|
||||
for (input) |c| {
|
||||
if (std.ascii.isWhitespace(c)) continue;
|
||||
try tokens.append(try lexToken(c));
|
||||
}
|
||||
queue[l_queue] = tok;
|
||||
l_queue += 1;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
fn spush(tok: StrTok) !void {
|
||||
if (l_stack >= MAX_STACK) {
|
||||
return error.StackOverflow;
|
||||
fn tiltUntil(
|
||||
operators: *std.ArrayList(Token),
|
||||
output: *std.ArrayList(Token),
|
||||
stop: std.meta.Tag(Token),
|
||||
) !bool {
|
||||
while (operators.items.len > 0) {
|
||||
const tok = operators.pop().?;
|
||||
if (std.meta.activeTag(tok) == stop) return true;
|
||||
try output.append(tok);
|
||||
}
|
||||
stack[l_stack] = tok;
|
||||
l_stack += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn spop() StrTok {
|
||||
if (l_stack == 0) {
|
||||
@panic("Stack underflow");
|
||||
}
|
||||
l_stack -= 1;
|
||||
return stack[l_stack];
|
||||
}
|
||||
fn shuntingYard(allocator: Allocator, tokens: []const Token) !std.ArrayList(Token) {
|
||||
var output = std.ArrayList(Token).init(allocator);
|
||||
errdefer output.deinit();
|
||||
var operators = std.ArrayList(Token).init(allocator);
|
||||
defer operators.deinit();
|
||||
|
||||
fn display(s: []const u8) !void {
|
||||
print("\x1b[1;1H\x1b[JText | {s}\n", .{s});
|
||||
print("Stack| ", .{});
|
||||
|
||||
for (stack[0..l_stack]) |item| {
|
||||
print("{s} ", .{item.s[0..item.len]});
|
||||
}
|
||||
|
||||
print("\nQueue| ", .{});
|
||||
for (queue[0..l_queue]) |item| {
|
||||
print("{s} ", .{item.s[0..item.len]});
|
||||
}
|
||||
|
||||
print("\n\n<press enter>\n", .{});
|
||||
var buf: [1]u8 = undefined;
|
||||
_ = try stdin.read(&buf);
|
||||
}
|
||||
|
||||
fn fail(s1: []const u8, s2: []const u8) !bool {
|
||||
print("[Error {s}] {s}\n", .{ s1, s2 });
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
// Helper function to check if a character is a digit
|
||||
fn isDigit(c: u8) bool {
|
||||
return c >= '0' and c <= '9';
|
||||
}
|
||||
|
||||
// Helper function to check if a character is a letter
|
||||
fn isAlpha(c: u8) bool {
|
||||
return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z');
|
||||
}
|
||||
|
||||
// Helper function to check if a character is alphanumeric or underscore
|
||||
fn isAlnum(c: u8) bool {
|
||||
return isAlpha(c) or isDigit(c) or c == '_';
|
||||
}
|
||||
|
||||
fn match(s: []const u8, patterns: []const Pattern, tok: *StrTok) ?struct { pattern: *const Pattern, end_pos: usize } {
|
||||
var pos: usize = 0;
|
||||
|
||||
// Skip whitespace
|
||||
while (pos < s.len and s[pos] == ' ') {
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if (pos >= s.len) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const remaining = s[pos..];
|
||||
|
||||
// Check for numeric literals
|
||||
if (remaining.len > 0 and (isDigit(remaining[0]) or
|
||||
((remaining[0] == '-' or remaining[0] == '+') and remaining.len > 1 and isDigit(remaining[1])) or
|
||||
(remaining[0] == '.' and remaining.len > 1 and isDigit(remaining[1])))) {
|
||||
|
||||
var i: usize = 0;
|
||||
var hasDot = false;
|
||||
var hasExp = false;
|
||||
|
||||
// Handle sign
|
||||
if (i < remaining.len and (remaining[i] == '-' or remaining[i] == '+')) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Process digits before decimal point
|
||||
while (i < remaining.len and isDigit(remaining[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Process decimal point and digits after it
|
||||
if (i < remaining.len and remaining[i] == '.') {
|
||||
hasDot = true;
|
||||
i += 1;
|
||||
while (i < remaining.len and isDigit(remaining[i])) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Process exponent
|
||||
if (i < remaining.len and (remaining[i] == 'e' or remaining[i] == 'E')) {
|
||||
hasExp = true;
|
||||
i += 1;
|
||||
|
||||
// Optional sign for exponent
|
||||
if (i < remaining.len and (remaining[i] == '-' or remaining[i] == '+')) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// There must be at least one digit in the exponent
|
||||
if (i < remaining.len and isDigit(remaining[i])) {
|
||||
i += 1;
|
||||
while (i < remaining.len and isDigit(remaining[i])) {
|
||||
i += 1;
|
||||
for (tokens) |token| {
|
||||
switch (token) {
|
||||
.digit => try output.append(token),
|
||||
.left_paren => try operators.append(token),
|
||||
.operator => |op| {
|
||||
while (operators.items.len > 0) {
|
||||
switch (operators.getLast()) {
|
||||
.left_paren => break,
|
||||
.operator => |top| {
|
||||
const same_prec_left = (top.precedence == op.precedence and
|
||||
op.is_left_associative);
|
||||
if (top.precedence > op.precedence or same_prec_left) {
|
||||
try output.append(operators.pop().?);
|
||||
} else break;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
} else if (hasExp) {
|
||||
// Invalid exponent
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
tok.s = remaining;
|
||||
tok.len = i;
|
||||
return .{ .pattern = &pat_arg[0], .end_pos = pos + i };
|
||||
try operators.append(token);
|
||||
},
|
||||
.right_paren => {
|
||||
if (!try tiltUntil(&operators, &output, .left_paren))
|
||||
return error.MismatchedRightParen;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Check for identifiers
|
||||
if (remaining.len > 0 and (isAlpha(remaining[0]) or remaining[0] == '_')) {
|
||||
var i: usize = 1;
|
||||
while (i < remaining.len and isAlnum(remaining[i])) {
|
||||
i += 1;
|
||||
}
|
||||
if (try tiltUntil(&operators, &output, .left_paren))
|
||||
return error.MismatchedLeftParen;
|
||||
|
||||
tok.s = remaining;
|
||||
tok.len = i;
|
||||
return .{ .pattern = &pat_arg[1], .end_pos = pos + i };
|
||||
}
|
||||
|
||||
// Check for operators and parentheses
|
||||
for (patterns) |p| {
|
||||
if (p.str.len == 0) break;
|
||||
|
||||
if (p.str[0] != '^') { // Not a special pattern like "number" or "identifier"
|
||||
if (remaining.len >= p.str.len and std.mem.eql(u8, remaining[0..p.str.len], p.str)) {
|
||||
tok.s = remaining;
|
||||
tok.len = p.str.len;
|
||||
return .{ .pattern = &p, .end_pos = pos + p.str.len };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
std.debug.assert(operators.items.len == 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
fn parse(text: []const u8) !bool {
|
||||
var tok = StrTok{ .s = undefined, .len = 0, .prec = 0, .assoc = 0 };
|
||||
var pos: usize = 0;
|
||||
fn calculate(allocator: Allocator, postfix: []const Token) !Number {
|
||||
var stack = std.ArrayList(Number).init(allocator);
|
||||
defer stack.deinit();
|
||||
|
||||
prec_booster = 0;
|
||||
l_queue = 0;
|
||||
l_stack = 0;
|
||||
|
||||
try display(text);
|
||||
|
||||
while (pos < text.len) {
|
||||
const match_arg = match(text[pos..], &pat_arg, &tok);
|
||||
if (match_arg == null) {
|
||||
return try fail("parse arg", text[pos..]);
|
||||
}
|
||||
|
||||
const p_arg = match_arg.?.pattern;
|
||||
pos = pos + (match_arg.?.end_pos - pos);
|
||||
|
||||
// Don't stack the parens
|
||||
if (std.mem.eql(u8, p_arg.str, "(")) {
|
||||
prec_booster += 100;
|
||||
} else {
|
||||
try qpush(tok);
|
||||
try display(text);
|
||||
}
|
||||
|
||||
// Check if we reached the end of the string
|
||||
if (pos >= text.len) {
|
||||
if (prec_booster != 0) {
|
||||
return try fail("unmatched (", "end of string");
|
||||
}
|
||||
|
||||
// If there are still operators on the stack, pop them all
|
||||
while (l_stack > 0) {
|
||||
try qpush(spop());
|
||||
try display(text);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
re_op: {
|
||||
const match_op = match(text[pos..], &pat_ops, &tok);
|
||||
if (match_op == null) {
|
||||
return try fail("parse op", text[pos..]);
|
||||
}
|
||||
|
||||
const p_op = match_op.?.pattern;
|
||||
pos = pos + (match_op.?.end_pos - pos);
|
||||
|
||||
tok.assoc = p_op.assoc;
|
||||
tok.prec = p_op.prec;
|
||||
|
||||
if (p_op.prec > 0) {
|
||||
tok.prec = p_op.prec + prec_booster;
|
||||
} else if (p_op.prec == -1) {
|
||||
if (prec_booster < 100) {
|
||||
return try fail("unmatched )", text[pos..]);
|
||||
}
|
||||
tok.prec = prec_booster;
|
||||
}
|
||||
|
||||
while (l_stack > 0) {
|
||||
const t = &stack[l_stack - 1];
|
||||
if (!((t.prec == tok.prec and t.assoc == Assoc.L) or t.prec > tok.prec)) {
|
||||
break;
|
||||
}
|
||||
try qpush(spop());
|
||||
try display(text);
|
||||
}
|
||||
|
||||
if (p_op.prec == -1) {
|
||||
prec_booster -= 100;
|
||||
break :re_op;
|
||||
}
|
||||
|
||||
if (p_op.prec == 0) {
|
||||
try display(text);
|
||||
if (prec_booster != 0) {
|
||||
return try fail("unmatched (", text[pos..]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
try spush(tok);
|
||||
try display(text);
|
||||
for (postfix) |token| {
|
||||
switch (token) {
|
||||
.digit => |n| try stack.append(n),
|
||||
.operator => |op| {
|
||||
if (stack.items.len < 2) return error.MissingOperand;
|
||||
const y = stack.pop().?;
|
||||
const x = stack.pop().?;
|
||||
try stack.append(op.apply(x, y));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
std.debug.assert(stack.items.len == 1);
|
||||
return stack.pop().?;
|
||||
}
|
||||
|
||||
// ── display helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
fn printTokens(tokens: []const Token, writer: anytype) !void {
|
||||
for (tokens, 0..) |tok, i| {
|
||||
if (i != 0) try writer.writeByte(' ');
|
||||
switch (tok) {
|
||||
.digit => |n| try writer.print("{d}", .{n}),
|
||||
.operator => |op| try writer.writeByte(op.token),
|
||||
.left_paren => try writer.writeByte('('),
|
||||
.right_paren => try writer.writeByte(')'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── entry point ───────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn main() !void {
|
||||
const tests = [_][]const u8{
|
||||
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3",
|
||||
"123",
|
||||
"3+4 * 2 / ( 1 - 5 ) ^ 2 ^ 3.14",
|
||||
"(((((((1+2+3**(4 + 5))))))",
|
||||
"a^(b + c/d * .1e5)", // Removed '!' which was causing an error
|
||||
"(1**2)**3",
|
||||
"2 + 2 *",
|
||||
};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const ally = gpa.allocator();
|
||||
const out = std.io.getStdOut().writer();
|
||||
|
||||
for (tests) |test_str| {
|
||||
print("Testing string `{s}' <enter>\n", .{test_str});
|
||||
var buf: [1]u8 = undefined;
|
||||
_ = try stdin.read(&buf);
|
||||
const input = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3";
|
||||
|
||||
const result = parse(test_str) catch |err| {
|
||||
switch (err) {
|
||||
error.ParseError => {
|
||||
print("string `{s}': Error\n\n", .{test_str});
|
||||
continue;
|
||||
},
|
||||
error.StackOverflow => {
|
||||
print("string `{s}': Stack Overflow Error\n\n", .{test_str});
|
||||
continue;
|
||||
},
|
||||
error.QueueOverflow => {
|
||||
print("string `{s}': Queue Overflow Error\n\n", .{test_str});
|
||||
continue;
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
var infix = try lex(ally, input);
|
||||
defer infix.deinit();
|
||||
|
||||
print("string `{s}': {s}\n\n", .{ test_str, if (result) "Ok" else "Error" });
|
||||
}
|
||||
var postfix = try shuntingYard(ally, infix.items);
|
||||
defer postfix.deinit();
|
||||
|
||||
try out.writeAll("infix: ");
|
||||
try printTokens(infix.items, out);
|
||||
try out.writeByte('\n');
|
||||
|
||||
try out.writeAll("postfix: ");
|
||||
try printTokens(postfix.items, out);
|
||||
try out.writeByte('\n');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue