8000 GitHub - earthfail/zeal: edn parser in zig
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

earthfail/zeal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zeal

edn parser and serializer in zig with faster parsing than standard clojure.edn/read1

Status

This is alpha software so expect bugs.

TODO

  • remove unnecessary allocator in lexer.zig
  • decrease allocations.

Usage

  1. add zeal to build.zig.zon:
.dependencies = .{
    ...
.zeal = .{
    .url = "https://github.com/earthfail/zeal/archive/v0.0.3.tar.gz",
    .hash = "1220335d9cb009618d8c1a7c1e099e64d6144b5d3021aa19fa90063879bef68c18ca",
    }
    ...
}
  1. add zeal to build.zig:
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    // define exe for your use case
    const exe = b.addExecutable(.{
        .name = "project-name",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    // add zeal as dependency
    const zeal = b.dependency("zeal", .{
        .optimize = optimize,
        .target = target,
    });
    exe.addModule("zeal", zeal.module("zeal"));
    b.installArtifact(exe);
    // rest of build.zig
  1. import in your code:
const zeal = @import("zeal");

Example

const std = @import("std");
const mem = std.mem;
const log = std.log;
const expect = std.testing.expect;
const zeal = @import("zeal");
const parser = zeal.parser;
const EdnReader = zeal.EdnReader;
const Edn = zeal.Edn;
const TagElement = zeal.TagElement;
const TagError = zeal.TagError;

/// simple function to read a line from user
/// taken from [ziglearn.org](https://ziglearn.org/chapter-2/#readers-and-writers) by [Sobeston](https://github.com/Sobeston)
fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 {
    var line = (try reader.readUntilDelimiterOrEof(
        buffer,
        '\n',
    )) orelse return null;
    // trim annoying windows-only carriage return character
    if (@import("builtin").os.tag == .windows) {
        return std.mem.trimRight(u8, line, "\r");
    } else {
        return line;
    }
}
// read edn from stdin
fn repl_edn() !void {
    var gpa = std.heap.GeneralPurposeAllocator(
    //.{ .verbose_log = true, .retain_metadata = true }
    .{}){};
    const g_allocator = gpa.allocator();
    defer {
        // _ = gpa.detectLeaks();

        const deinit_status = gpa.deinit();
        if (deinit_status == .leak) expect(false) catch {
            @panic("gpa leaked");
        };
    }

    const stdout = std.io.getStdOut().writer();
    const stdin = std.io.getStdIn().reader();
    // buffer to hold user input
    var buffer: [2000]u8 = undefined;
    try stdout.writeAll("edn is an extensible data notation\n");
    while (true) {
        try stdout.print("reading input:", .{});
        if (try nextLine(stdin, &buffer)) |input| {
            defer {
                if (gpa.detectLeaks()) {
                    std.debug.print("gpa detected leaks with input '{s}'\n", .{input});
                }
            }

            // define ednreader
            var reader = EdnReader.init(g_allocator, input);
            // initialize readers to tagged elements
            reader.data_readers = std.StringHashMap(parser.TagHandler).init(g_allocator);
            // define reader for #inst elements.
            try reader.data_readers.?.put("inst", edn_to_inst);
            defer reader.deinit();

            if (reader.readEdn()) |edn| {
                // try stdout.print("{}\n", .{edn.*});
                // convert edn to []const u8
                defer edn.deinit(g_allocator);
                const serialize = try parser.Edn.serialize(edn.*, g_allocator);
                defer g_allocator.free(serialize);

                try stdout.print("{s}\n", .{serialize});

            } else |err| {
                try stdout.print("got error parsing input {}. Salam\n", .{err});
                // break;
            }
        } else break;
    }
    try stdout.print("finished\n", .{});
}
/// given a integer in Edn form, returns a tagged element with that integer plus ten
fn edn_to_inst(allocator: mem.Allocator, edn: Edn) parser.TagError!*TagElement {
    switch (edn) {
        .integer => |i| {
            var i_p = try allocator.create(@TypeOf(i));
            i_p.* = i + 10;
            var ele = try allocator.create(TagElement);
            ele.pointer = @intFromPtr(i_p);
            ele.deinit = inst_deinit;
            ele.serialize = inst_serialize;
            return ele;
        },
        else => {
            return TagError.TypeNotSupported;
        },
    }
}
fn inst_deinit(pointer: usize, allocator: mem.Allocator) void {
    const i_p: *i64 = @ptrFromInt(pointer);
    allocator.destroy(i_p);
}
// specifiy how to convert data back to string
fn inst_serialize(pointer: usize, allocator: mem.Allocator) parser.SerializeError![]const u8 {
    const i_p: *i64 = @ptrFromInt(pointer);
    var buffer = std.ArrayList(u8).init(allocator);
    errdefer buffer.deinit();

    const writer = buffer.writer();
    writer.print("{d}", .{i_p.*}) catch return parser.SerializeError.InvalidData;
    return buffer.toOwnedSlice();
}
pub fn main() !void {
    try repl_edn();
}

Footnotes

  1. tested on json64KB converted to edn with (spit "64KB.edn" data) and zig build -Doptimize=ReleaseFast

About

edn parser in zig

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

0