Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
|
|
@ -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(" ")
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
]
|
||||
|
|
@ -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