实现一个支持加减乘除、括号的四则运算解析器,可进行整数和浮点数运算。
实现代码
main.zig
zig
const std = @import("std");
const Parser = @import("Parser.zig");
pub fn main() void {
var parser = Parser.init("(1 + 0.5) * 10 - 5 / 2 + (3 * (2 + 1))");
const value = parser.exec() catch |e| {
printError(&parser, e);
return;
};
std.debug.print("{}\n", .{value});
}
fn printError(parser: *const Parser, err: Parser.ParseError) void {
const err_pos = parser.lex.pos -| 1;
const window_s = err_pos -| 20;
const window_e = @min(err_pos +| 20, parser.lex.src.len);
const window = parser.lex.src[window_s..window_e];
std.debug.print("{s}\n", .{window});
for (0..err_pos - window_s) |_| {
std.debug.print(" ", .{});
}
std.debug.print("^ {}\n", .{err});
}
Parser.zig
zig
const std = @import("std");
lex: Lexer,
cur: Token,
const Self = @This();
pub const ParseError = error{
Syntax,
Lexical,
};
pub fn init(src: []const u8) Self {
return .{
.lex = .init(src),
.cur = undefined,
};
}
pub fn exec(self: *Self) ParseError!f64 {
self.cur = try self.lex.nextToken();
const num = try self.parseExpr();
if (self.cur.kind() != .eof) {
return ParseError.Syntax;
}
return num;
}
// 消费匹配的 Token
fn eat(self: *Self, kind: TokenKind) ParseError!void {
if (self.cur.kind() != kind) {
return ParseError.Syntax;
}
self.cur = try self.lex.nextToken();
}
// 解析因子
fn parseFactor(self: *Self) ParseError!f64 {
switch (self.cur.data) {
.num => |num| {
try self.eat(.num);
return num;
},
.lparen => {
try self.eat(.lparen);
const num = try self.parseExpr();
try self.eat(.rparen);
return num;
},
else => {
return ParseError.Syntax;
},
}
}
// 解析乘除
fn parseTerm(self: *Self) ParseError!f64 {
var num = try self.parseFactor();
while (true) {
switch (self.cur.kind()) {
.mul => {
try self.eat(.mul);
num *= try self.parseFactor();
},
.div => {
try self.eat(.div);
num /= try self.parseFactor();
},
else => break,
}
}
return num;
}
// 解析加减
fn parseExpr(self: *Self) ParseError!f64 {
var num = try self.parseTerm();
while (true) {
switch (self.cur.kind()) {
.add => {
try self.eat(.add);
num += try self.parseTerm();
},
.sub => {
try self.eat(.sub);
num -= try self.parseTerm();
},
else => break,
}
}
return num;
}
const Token = struct {
data: TokenData,
fn kind(self: *const Token) TokenKind {
return self.*.data;
}
};
const TokenData = union(TokenKind) {
/// 数字
num: f64,
/// `+`
add,
/// `-`
sub,
/// `*`
mul,
/// `/`
div,
/// `(`
lparen,
/// `)`
rparen,
/// 结束
eof,
};
const TokenKind = enum {
/// 数字
num,
/// `+`
add,
/// `-`
sub,
/// `*`
mul,
/// `/`
div,
/// `(`
lparen,
/// `)`
rparen,
/// 结束
eof,
};
const Lexer = struct {
src: []const u8,
pos: usize = 0,
fn init(src: []const u8) Lexer {
return .{ .src = src };
}
// 获取当前字符
fn peek(self: *Lexer) ?u8 {
if (self.pos >= self.src.len) {
return null;
}
return self.src[self.pos];
}
// 前进一格
fn next(self: *Lexer) void {
if (self.pos < self.src.len) {
self.pos += 1;
}
}
// 跳过空格
fn skipSpace(self: *Lexer) void {
while (self.peek()) |cur| : (self.next()) {
if (!std.ascii.isWhitespace(cur)) {
return;
}
}
}
// 读取一个数字
fn readNum(self: *Lexer) ?f64 {
const start = self.pos;
while (self.peek()) |cur| : (self.next()) {
if (!std.ascii.isDigit(cur) and cur != '.') {
break;
}
}
return std.fmt.parseFloat(f64, self.src[start..self.pos]) catch null;
}
// 取下一个 Token
fn nextToken(self: *Lexer) ParseError!Token {
self.skipSpace();
if (self.peek()) |cur| {
return switch (cur) {
'+' => {
self.next();
return .{ .data = .add };
},
'-' => {
self.next();
return .{ .data = .sub };
},
'*' => {
self.next();
return .{ .data = .mul };
},
'/' => {
self.next();
return .{ .data = .div };
},
'(' => {
self.next();
return .{ .data = .lparen };
},
')' => {
self.next();
return .{ .data = .rparen };
},
'0'...'9', '.' => {
const num = self.readNum() orelse return ParseError.Lexical;
return .{ .data = .{ .num = num } };
},
else => ParseError.Lexical,
};
} else {
return .{ .data = .eof };
}
}
};