Data update

This commit is contained in:
Ingy döt Net 2026-04-30 12:34:36 -04:00
parent 4bb20c9b71
commit cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions

View file

@ -0,0 +1,46 @@
OPS = {
"^" => { prio: 4, assoc: :right },
"*" => { prio: 3, assoc: :left },
"/" => { prio: 3, assoc: :left },
"+" => { prio: 2, assoc: :left },
"-" => { prio: 2, assoc: :left },
}
def to_rpn (tokens)
output = [] of String | Float64
opstack = [] of String
tokens.each do |token|
case token
when .is_a?(Float64) then output << token
when "(" then opstack << token
when ")"
while opstack.present? && opstack.last != "("
output << opstack.pop
end
opstack.pop
else
opdata = OPS[token]? || raise "Unknown operator '#{token}'"
while opstack.present? && OPS.has_key?(opstack.last) &&
(opdata[:prio] < OPS[opstack.last][:prio] ||
opdata[:prio] == OPS[opstack.last][:prio] && opdata[:assoc] == :left)
output << opstack.pop
end
opstack << token
end
end
while opstack.present?
output << opstack.pop
end
output
end
def tokenize (s)
s.split.map {|t| t.to_f rescue t }
end
infix = tokenize "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
rpn = to_rpn infix
puts infix.join(" ")
puts rpn.join(" ")

View file

@ -0,0 +1,84 @@
import 'dart:collection';
class ShuntingYard {
static final Map<String, OperatorInfo> operators = {
'^': OperatorInfo('^', 4, true),
'*': OperatorInfo('*', 3, false),
'/': OperatorInfo('/', 3, false),
'+': OperatorInfo('+', 2, false),
'-': OperatorInfo('-', 2, false),
};
static String toPostfix(String infix) {
List<String> tokens = infix.split(' ');
Queue<String> stack = Queue<String>();
List<String> output = [];
void printState(String action) {
List<String> stackList = stack.toList();
stackList = stackList.reversed.toList();
print('${(action + ":").padRight(4)} ${"stack[ ${stackList.join(" ")} ]".padRight(18)} out[ ${output.join(" ")} ]');
}
for (String token in tokens) {
if (isNumeric(token)) {
output.add(token);
printState(token);
} else if (operators.containsKey(token)) {
OperatorInfo op1 = operators[token]!;
while (stack.isNotEmpty && operators.containsKey(stack.last)) {
OperatorInfo op2 = operators[stack.last]!;
int c = op1.precedence.compareTo(op2.precedence);
if (c < 0 || (!op1.rightAssociative && c <= 0)) {
output.add(stack.removeLast());
} else {
break;
}
}
stack.add(token);
printState(token);
} else if (token == "(") {
stack.add(token);
printState(token);
} else if (token == ")") {
String top = "";
while (stack.isNotEmpty && (top = stack.removeLast()) != "(") {
output.add(top);
}
if (top != "(") throw ArgumentError("No matching left parenthesis.");
printState(token);
}
}
while (stack.isNotEmpty) {
String top = stack.removeLast();
if (!operators.containsKey(top)) throw ArgumentError("No matching right parenthesis.");
output.add(top);
}
printState("pop");
return output.join(" ");
}
static bool isNumeric(String str) {
try {
int.parse(str);
return true;
} catch (e) {
return false;
}
}
}
class OperatorInfo {
final String symbol;
final int precedence;
final bool rightAssociative;
OperatorInfo(this.symbol, this.precedence, this.rightAssociative);
}
void main() {
String infix = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3";
print(ShuntingYard.toPostfix(infix));
}

View file

@ -0,0 +1,54 @@
local buffer = require "pluto:buffer"
require "queue"
local ops <const> = "-+/*^"
--[[ To find out the precedence, we take the 0-based index of the
token in the OPS string and divide by 2 (rounding down).
This will give us: 0, 0, 1, 1, 2
]]
local function infix_to_postfix(infix)
local sb = new buffer()
local s = new stack()
for token in infix:gmatch("%S+") do
local c = token[1]
local idx = ops:find(c, 1, true)
-- Check for operator.
if idx then
if s:empty() then
s:push(idx)
else
while !s:empty() do
local prec2 = (s:peek() - 1) // 2
local prec1 = (idx - 1) // 2
if prec2 > prec1 or (prec2 == prec1 and c != "^") then
sb:append(ops[s:pop()] .. " ")
else
break
end
end
s:push(idx)
end
elseif c == "(" then
s:push(-2) -- -2 stands for "("
elseif c == ")" then
-- Until "(" on stack, pop operators.
while s:peek() != -2 do sb:append(ops[s:pop()] .. " ") end
s:pop()
else
sb:append(token .. " ")
end
end
while !s:empty() do sb:append(ops[s:pop()] .. " ") end
return sb:tostring()
end
local es = {
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3",
"( ( 1 + 2 ) ^ ( 3 + 4 ) ) ^ ( 5 + 6 )"
}
for es as e do
print($"Infix : {e}")
print($"Postfix : {infix_to_postfix(e)}\n")
end

View file

@ -0,0 +1,91 @@
#' Parses an infix expression string into Reverse Polish Notation (RPN).
#'
#' This function implements the Shunting-yard algorithm to convert a space-separated
#' infix mathematical expression into a space-separated RPN expression.
#'
#' @param s A string containing the infix expression, with tokens separated by spaces.
#' @return A character vector representing the expression in RPN.
#' @examples
#' teststring <- "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
#' rpn_result <- parseinfix2rpn(teststring)
#' cat("\nResult:", teststring, "becomes", paste(rpn_result, collapse = " "), "\n")
parseinfix2rpn <- function(s) {
# Initialize output queue and operator stack as character vectors
outputq <- c()
opstack <- c()
# Split the input string into tokens
infix <- strsplit(s, " ")[[1]]
# Define operator precedence. Higher number means higher precedence.
# This mirrors Julia's precedence for the operators used.
precedence <- list(
`+` = 10, `-` = 10,
`*` = 20, `/` = 20,
`^` = 30
)
for (tok in infix) {
# Check if the token is a number (integer or decimal)
if (!is.na(as.numeric(tok))) {
outputq <- c(outputq, tok)
} else if (tok == "(") {
opstack <- c(opstack, tok)
} else if (tok == ")") {
# Pop operators from the stack to the output until a "(" is found
while (length(opstack) > 0) {
op <- opstack[length(opstack)]
opstack <- opstack[-length(opstack)]
if (op == "(") {
break # Discard the "(" and stop
}
outputq <- c(outputq, op)
}
} else { # Operator
# While there is an operator at the top of the stack with higher precedence,
# or with the same precedence and the current operator is not right-associative (^),
# pop it to the output queue.
while (length(opstack) > 0) {
top_op <- opstack[length(opstack)]
# Stop if the top of the stack is a parenthesis
if (top_op == "(") {
break
}
# Compare precedence
if (precedence[[top_op]] > precedence[[tok]] ||
(precedence[[top_op]] == precedence[[tok]] && tok != "^")) {
# Pop from opstack and push to outputq
opstack <- opstack[-length(opstack)]
outputq <- c(outputq, top_op)
} else {
# The operator on the stack has lower precedence, so stop.
break
}
}
# Push the current operator onto the stack
opstack <- c(opstack, tok)
}
# Debug print statement (equivalent to Julia's println)
cat("The working output stack is", paste(outputq, collapse = " "), "\n")
}
# After the loop, pop any remaining operators from the stack to the output
while (length(opstack) > 0) {
op <- opstack[length(opstack)]
opstack <- opstack[-length(opstack)]
if (op == "(") {
stop("mismatched parentheses")
}
outputq <- c(outputq, op)
}
return(outputq)
}
# --- Test the function ---
teststring <- "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
rpn_result <- parseinfix2rpn(teststring)
cat("\nResult:", teststring, "becomes", paste(rpn_result, collapse = " "), "\n")

View file

@ -0,0 +1,113 @@
Rebol [
title: "Rosetta code: Parsing/Shunting-yard algorithm"
file: %Parsing-Shunting-yard_algorithm.r3
url: https://rosettacode.org/wiki/Parsing-Shunting-yard_algorithm
needs: 3.15.0 ;; or something like that
note: "Based on Red language solution (Hinjolicious)!"
purpose: {Shunting-Yard Infix to RPN - Hinjo, July 2025}
]
; no error handling, assumed expr is spaced and correct
shunting: function/with [expr [string!] /trace] [
output: copy [] ops: copy []
stats: either trace [:print-stats][none]
tokens: split expr " "
while [not empty? tokens] [
tok: first tokens tokens: next tokens
case [
find "0123456789" tok [ ; add numbers to output
act: "(a) Add number to output"
append output tok
stats tok
]
find "(" tok [ ; push "(" to stack
act: "(b) Push ( to stack"
append ops tok
stats tok
]
find ")" tok [ ; pop stack to output until "("
while [(last ops) <> "("] [ ; assume input is correct!
act: "(c) Pop op to output"
append output (take/last ops)
stats tok
]
act: "(d) Discard ( from stack"
take/last ops
stats SP ; should be ")"
]
find "+-*/^^" tok [
; Pop higher or equal-left operators from stack
while [not empty? ops] [
last-op: last ops
if any [
none? last-op
last-op == "(" ; skip, we're in ()
prec/:last-op < prec/:tok
asc/:last-op <> "L"
][ break ]
act: "(e) Pop higher/equal op to output" append output take/last ops stats SP
]
; Now push current operator
act: "(f) Push op to stack"
append ops tok
stats tok
]
]
]
; flush the stack to output
act: "Finished. Flush ops:" stats SP
while [not empty? ops] [
act: "(g) Pop op to output"
append output (take/last ops)
stats SP
]
; return output
form output
][
output: ops: tokens: act: none
prec: #["+" 2 "-" 2 "*" 3 "/" 3 "^^" 4 ] ; precedence
asc: #["+" "L" "-" "L" "*" "L" "/" "L" "^^" "R"] ; association
print-stats: function [t] [ ; display basic tracing
print [t "|" pad act 35 "|" pad (form output) 30 pad (reverse form ops) -15]
]
]
context [
; assumed input is space separated! ('^' is escape char!)
print "Some test for input and output with trace:"
print ["^/" s: "3 + 4 * 2 / ( 1 - 5 ) ^^ 2 ^^ 3"] shunting/trace s
print ["^/" s: "( ( 1 + 2 ) ^^ ( 3 + 4 ) ) ^^ ( 5 + 6 )"] shunting/trace s
print ["^/" s: "1 + 2 * 3 / 4 + 5"] shunting/trace s
check: function [s t u][
print [pad s 40 "|" pad t 30 "|" pad u 30 "|" t = u]
]
print "^/Print input, validation and the output:"
s: "1 + 2 * 3 / 4 + 5" t: "1 2 3 * 4 / + 5 +" u: shunting s check s t u
s: "3 + 4 * 2 / ( 1 - 5 ) ^^ 2 ^^ 3" t: "3 4 2 * 1 5 - 2 3 ^^ ^^ / +" u: shunting s check s t u
s: "( ( 1 + 2 ) ^^ ( 3 + 4 ) ) ^^ ( 5 + 6 )" t: "1 2 + 3 4 + ^^ 5 6 + ^^" u: shunting s check s t u
s: "( 1 + 2 ) ^^ ( 3 + 4 ) ^^ ( 5 + 6 )" t: "1 2 + 3 4 + 5 6 + ^^ ^^" u: shunting s check s t u
s: "( ( 3 ^^ 4 ) ^^ 2 ^^ 9 ) ^^ 2 ^^ 5" t: "3 4 ^^ 2 9 ^^ ^^ 2 5 ^^ ^^" u: shunting s check s t u
s: "( 1 + 4 ) * ( 5 + 3 ) * 2 * 3" t: "1 4 + 5 3 + * 2 * 3 *" u: shunting s check s t u
s: "1 * 2 * 3 * 4" t: "1 2 * 3 * 4 *" u: shunting s check s t u
s: "1 + 2 + 3 + 4" t: "1 2 + 3 + 4 +" u: shunting s check s t u
s: "( 1 + 2 ) ^^ ( 3 + 4 )" t: "1 2 + 3 4 + ^^" u: shunting s check s t u
s: "( 5 ^^ 6 ) ^^ 7" t: "5 6 ^^ 7 ^^" u: shunting s check s t u
s: "5 ^^ 4 ^^ 3 ^^ 2" t: "5 4 3 2 ^^ ^^ ^^" u: shunting s check s t u
s: "1 + 2 + 3" t: "1 2 + 3 +" u: shunting s check s t u
s: "1 ^^ 2 ^^ 3" t: "1 2 3 ^^ ^^" u: shunting s check s t u
s: "( 1 ^^ 2 ) ^^ 3" t: "1 2 ^^ 3 ^^" u: shunting s check s t u
s: "1 - 1 + 3" t: "1 1 - 3 +" u: shunting s check s t u
s: "3 + 1 - 1" t: "3 1 + 1 -" u: shunting s check s t u
s: "1 - ( 2 + 3 )" t: "1 2 3 + -" u: shunting s check s t u
s: "4 + 3 + 2" t: "4 3 + 2 +" u: shunting s check s t u
s: "5 + 4 + 3 + 2" t: "5 4 + 3 + 2 +" u: shunting s check s t u
s: "5 * 4 * 3 * 2" t: "5 4 * 3 * 2 *" u: shunting s check s t u
s: "5 + 4 - ( 3 + 2 )" t: "5 4 + 3 2 + -" u: shunting s check s t u
s: "3 - 4 * 5" t: "3 4 5 * -" u: shunting s check s t u
s: "3 * ( 4 - 5 )" t: "3 4 5 - *" u: shunting s check s t u
s: "( 3 - 4 ) * 5" t: "3 4 - 5 *" u: shunting s check s t u
s: "4 * 2 + 1 - 5" t: "4 2 * 1 + 5 -" u: shunting s check s t u
s: "4 * 2 / ( 1 - 5 ) ^^ 2" t: "4 2 * 1 5 - 2 ^^ /" u: shunting s check s t u
]

View file

@ -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');
}