1e1195e27a11 — Nathan Michaels 1 year, 3 months ago
Example code for RPN calculator.

The Python code is actually runnable both interactively and as a
command line calculator. The Zig code doesn't bother with stdin or
stdout, and just has tests that push values around.
4 files changed, 185 insertions(+), 0 deletions(-)

A => Makefile
A => rpn.py
A => rpn.zig
A => stack.zig
A => Makefile +4 -0
@@ 0,0 1,4 @@ 
+.PHONY: test
+
+test: rpn.zig stack.zig
+	zig test rpn.zig

          
A => rpn.py +57 -0
@@ 0,0 1,57 @@ 
+#!/usr/bin/env python3
+
+import sys
+
+
+def str2float(str):
+    try:
+        return float(str)
+    except ValueError:
+        return None
+
+
+ops = {
+    "+": lambda a, b: a + b,
+    "-": lambda a, b: a - b,
+    "*": lambda a, b: a * b,
+    "/": lambda a, b: a / b,
+}
+
+
+def rpn(cmd, vals):
+    val = str2float(cmd)
+    if val is not None:
+        vals.append(val)
+        return None
+    op = ops[cmd]
+    b = vals.pop()
+    a = vals.pop()
+    vals.append(op(a, b))
+    return vals[-1]
+
+
+def repl():
+    vals = []
+    while True:
+        try:
+            line = input()
+        except EOFError:
+            return
+        val = rpn(line, vals)
+        print(line, vals, file=sys.stderr)
+        if val is not None:
+            print(val)
+
+
+def cli():
+    vals = []
+    for cmd in sys.argv[1:]:  # Note that your shell might need to escape '*'
+        rpn(cmd, vals)
+    print(vals.pop())
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        cli()
+    else:
+        repl()

          
A => rpn.zig +61 -0
@@ 0,0 1,61 @@ 
+const std = @import("std");
+const Stack = @import("stack.zig").Stack;
+
+fn str2f64(s: []const u8) ?f64 {
+    return std.fmt.parseFloat(f64, s) catch return null;
+}
+
+fn add(a: f64, b: f64) f64 {
+    return a + b;
+}
+
+fn sub(a: f64, b: f64) f64 {
+    return a - b;
+}
+
+fn mul(a: f64, b: f64) f64 {
+    return a * b;
+}
+
+fn div(a: f64, b: f64) f64 {
+    return a / b;
+}
+
+fn rpn(cmd: []const u8, vals: *Stack) !?f64 {
+    if (str2f64(cmd)) |val| {
+        try vals.push(val);
+        return null;
+    }
+
+    const b = vals.pop().?;
+    const a = vals.pop().?;
+
+    var result = switch (cmd[0]) {
+        '+' => add(a, b),
+        '-' => sub(a, b),
+        '*' => mul(a, b),
+        '/' => div(a, b),
+        else => return error.BadOp,
+    };
+
+    try vals.push(result);
+    return vals.peek();
+}
+
+const testing = std.testing;
+const alloc = std.testing.allocator;
+
+test "Reverse some polish notations." {
+    var stack: Stack = Stack.init(alloc);
+    defer stack.deinit();
+
+    try testing.expectEqual(@as(?f64, null), try rpn("3.5", &stack));
+    try testing.expectEqual(@as(?f64, null), try rpn("5.5", &stack));
+    try testing.expectEqual(@as(?f64, 9.0), try rpn("+", &stack));
+    try testing.expectEqual(@as(?f64, null), try rpn("83", &stack));
+    try testing.expectEqual(@as(?f64, null), try rpn("3", &stack));
+    _ = try rpn("/", &stack);
+    try testing.expectEqual(@as(?f64, 249.0), try rpn("*", &stack));
+    try testing.expectEqual(@as(?f64, null), try rpn("49", &stack));
+    try testing.expectEqual(@as(?f64, 200.0), try rpn("-", &stack));
+}

          
A => stack.zig +63 -0
@@ 0,0 1,63 @@ 
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+pub const Stack = struct {
+    const Self = @This();
+
+    pub const Node = struct {
+        next: ?*Node = null,
+        data: f64,
+    };
+
+    head: ?*Node = null,
+    allocator: Allocator,
+
+    pub fn push(self: *Self, val: f64) !void {
+        const next: ?*Node = self.head;
+        var new: *Node = try self.allocator.create(Node);
+        new.next = next;
+        new.data = val;
+        self.head = new;
+    }
+
+    pub fn pop(self: *Self) ?f64 {
+        var popped: *Node = self.head orelse return null;
+        defer self.allocator.destroy(popped);
+        self.head = popped.next;
+        return popped.data;
+    }
+
+    pub fn peek(self: *Self) ?f64 {
+        var next: *Node = self.head orelse return null;
+        return next.data;
+    }
+
+    pub fn init(allocator: Allocator) Self {
+        return Self{
+            .head = null,
+            .allocator = allocator,
+        };
+    }
+
+    pub fn deinit(self: *Self) void {
+        while (self.pop()) |_| {}
+    }
+};
+
+const testing = std.testing;
+const alloc = std.testing.allocator;
+
+test "Create, push, pop" {
+    var stack: Stack = Stack.init(alloc);
+    try std.testing.expectEqual(@as(?f64, null), stack.peek());
+    try std.testing.expectEqual(@as(?f64, null), stack.pop());
+    try stack.push(2.5);
+    try std.testing.expectEqual(@as(?f64, 2.5), stack.peek());
+    try stack.push(3.5);
+    try std.testing.expectEqual(@as(?f64, 3.5), stack.peek());
+    try std.testing.expectEqual(@as(?f64, 3.5), stack.pop());
+    try std.testing.expectEqual(@as(?f64, 2.5), stack.peek());
+    try std.testing.expectEqual(@as(?f64, 2.5), stack.pop());
+    try std.testing.expectEqual(@as(?f64, null), stack.peek());
+    try std.testing.expectEqual(@as(?f64, null), stack.pop());
+}