Open34

Zig

erukitierukiti

最初から付けておけば良かったと思いつつ、まぁいいやってことで書き始める。

erukitierukiti

zig run hoge.zig とか zig test hoge.zig ができるのほんと素晴らしい。

VSCode の拡張は色々足りない感じだけどまぁ仕方なさそう。

erukitierukiti

ひたすら https://ziglang.org/documentation/0.9.1/ を読んでいる。

C言語の演算子は、マシン語・アセンブリに比べて弱いけど、Zig はそこらへんが拡充されてる分Cよりもチューニングの余地がありそう。

.{hoge} ってなんじゃい?と思ったが、どうやら無名 struct 的なやつかな。
struct 周りが読んでてなんじゃこりゃ感あふれる。

erukitierukiti

型定義が const Type = struct { x: u32 }; みたいになってるのなんか納得いかない。なんで const なの????

In Zig, types are first-class citizens.
ふーむ

erukitierukiti
    const Payload = union {
        int: i64,
        float: f64,
        boolean: bool,
    };
    const st = Payload{ .int = 1024 };
    print("{}\n", .{st.float});

あれ、union は C言語的な union ではないのか

./hello.zig:19:23: error: accessing union field 'float' while field 'int' is set
    print("{}\n", .{st.float});
                      ^

packed union ならワンチャンって思ったけどだめっぽい

erukitierukiti
   while (eventuallyNullSequence()) |value| {
        sum1 += value;
    }

うわ、この発想はなかった。なるほど????

erukitierukiti

while の else |err| はなんかやりすぎ感というかせめて catch |err| にすれば良かったのでは?

erukitierukiti

for (items) |value| { これも while と同じくなんだろうけど、この字面に慣れてないと読みづらいよなー

erukitierukiti
var v: u32 = undefined

が通るの納得いかねぇ

var v: ?u32 = undefined

なら分かるんだけど

erukitierukiti

というか undefinednull の両方があるのなんでなの

erukitierukiti

How do I include one .zig file from another .zig file - Stack Overflow

// p.zig
const std = @import("std");
const print = std.debug.print;

pub fn p(comptime format: []const u8, args: anytype) void {
    print(format, args);
}
// main.zig

const p = @import("p.zig").p;

/// main routine
pub fn main() !void {
    p("{}\n", .{1});
}

import するとき .zig をつけないといけない。あと JS みたいに const { p } = @import... 表記はできないのが少し残念

erukitierukiti

Zig の文字列よくわからん。

const std = @import("std");
const expect = std.testing.expect;
const print = std.debug.print;

pub fn EndOfFile(source: []const u8) u32 {
    return if (source.len == 0) 1 else 0;
}

pub fn EndOfLine(source: []const u8) u32 {
    if (source.len == 0) return 0;
    if (source.len >= 2 and source[0] == '\r' and source[1] == '\n') return 2;
    if (source[0] == '\n' or source[0] == '\r') return 1;
    return 0;
}

pub fn Space(source: []const u8) u32 {
    if (source.len == 0) return 0;
    if (source[0] == ' ') return 1;
    if (source[0] == '\t') return 1;
    return EndOfLine(source);
}

test {
    try expect(EndOfFile("") == 1);
    try expect(EndOfFile("a") == 0);

    try expect(EndOfLine("\n") == 1);
    try expect(EndOfLine("\r") == 1);
    try expect(EndOfLine("\r\n") == 2);
    try expect(EndOfLine("") == 0);
    try expect(EndOfLine("a") == 0);

    try expect(Space(" ") == 1);
    try expect(Space("\t") == 1);
    try expect(Space("\n") == 1);
    try expect(Space("\r") == 1);
    try expect(Space("\r\n") == 2);
    try expect(Space("a") == 0);
    try expect(Space("") == 0);
}

ここまではうまくいったが、

pub fn Comment(source: []const u8) u32 {
    if (source.len == 0) return 0;
    if (source[0] != '#') return 0;
    var i = 1;
    while (EndOfLine(source[i..])) |n| {
        if (n or source.len >= n) {
            break;
        }
        i += 1;
    }
    return i;
}

test {
    try expect(Comment("") == 0);
}

ってコードを書こうとすると var i は comptime か const じゃないとだめって怒られる。

pub fn Comment(source: []const u8) u32 {
    if (source.len == 0) return 0;
    if (source[0] != '#') return 0;
    const ptr = &source;
    ptr += 1;
    while (EndOfLine(ptr.*)) |n| {
        if (n or source.len >= n) {
            break;
        }
        ptr += 1;
    }
    return ptr;
}

test {
    try expect(Comment("") == 0);
}

だと integer value 1 cannot be coerced to type '*const []const u8' になる。

よくある C でのポインタ操作やりたいだけなんが。まぁたぶん source []const u8 がだめなんだろうなぁとは思いつつ

erukitierukiti

とりあえずstr型を用意してみる。

const str = []const u8;
pub fn EndOfFile(source: str) u32 {
    return if (source.len == 0) 1 else 0;
}

str をいじりまくってみるかな

erukitierukiti
  • []T - pointer to runtime-known number of items.
    • runtime-known ほう。これって comptime ってことよね
erukitierukiti

[*]T - many-item pointer to unknown number of items.
unknown にはなるけど、これは source.len がとれなくなる。まぁそりゃそうか

erukitierukiti

ASCIIZ(0 terminated string)にしてみるテスト

const str = [*:0]const u8;

pub fn EndOfFile(source: str) u32 {
    if (source[0] == 0) return 1 else return 0;
}

pub fn EndOfLine(source: str) u32 {
    if (source[0] == 0) return 0;
    if (source[1] != 0 and source[0] == '\r' and source[1] == '\n') return 2;
    if (source[0] == '\n' or source[0] == '\r') return 1;
    return 0;
}

これでも

pub fn Comment(source: str) u32 {
    if (source[0] == 0) return 0;
    if (source[0] != '#') return 0;
    const ptr = &source;
    ptr += 1;

しようとすると、integer value 1 cannot be coerced to type 'const [:0]const u8'

ファッ!?

erukitierukiti

やりたいことは、引数に index も渡せば問題なく実現できるんだろうけどさぁ

ハー。ぽいんた分かんねぇ(この言葉言うことになる時代が来るとは思わなかった)

ryoppippiryoppippi

こちらでいかがでしょうか

pub fn Comment(source: []const u8) u32 {
    if (source.len == 0) return 0;
    if (source[0] != '#') return 0;
    {
        var i: u32 = 1;
        while (i < source.len) : (i += 1) {
            const n = source[i..];
            if (EndOfLine(n) != 0) return i;
        }
        return i;
    }
}

test {
    try expect(Comment("") == 0);
    try expect(Comment("hello") == 0);
    try expect(Comment("#hello") == 6);
}
erukitierukiti

Zig だとポインタを足したり引いたりせず、slice 使う方が望ましいのかなー
うーん、分からん。

erukitierukiti
const str = [:0]const u8;

pub fn EndOfFile(source: str) ?u32 {
    if (source[0] == 0) return 1 else return null;
}
test "end of file" {
    try expect(EndOfFile("").? == 1);
    try expect(EndOfFile("a") == null);
}

pub fn EndOfLine(source: str) ?u32 {
    if (source[0] == 0) return null;
    if (source[1] != 0 and source[0] == '\r' and source[1] == '\n') return 2;
    if (source[0] == '\n' or source[0] == '\r') return 1;
    return null;
}

test "end of line" {
    try expect(EndOfLine("\n").? == 1);
    try expect(EndOfLine("\r").? == 1);
    try expect(EndOfLine("\r\n").? == 2);
    try expect(EndOfLine("") == null);
    try expect(EndOfLine("a") == null);
}
pub fn Space(source: str) ?u32 {
    if (source[0] == 0) return null;
    if (source[0] == ' ') return 1;
    if (source[0] == '\t') return 1;
    return EndOfLine(source);
}

test "space" {
    try expect(Space(" ").? == 1);
    try expect(Space("\t").? == 1);
    try expect(Space("\n").? == 1);
    try expect(Space("\r").? == 1);
    try expect(Space("\r\n").? == 2);
    try expect(Space("a") == null);
    try expect(Space("") == null);
}

pub fn Comment(source: str) ?u32 {
    if (source[0] == 0) return null;
    if (source[0] != '#') return null;
    var i: u32 = 1;
    while (EndOfLine(source[i..]) == null) {
        i += 1;
    }
    if (EndOfLine(source[1..])) |n| {
        return i + n;
    } else {
        return null;
    }
}
pub fn main() void {
    print("{}", .{Comment("#hoge\n")});
}
test {
    try expect(Comment("") == null);
    try expect(Comment("#") == null);
    try expect(Comment("#hoge\n").? == 5);
}

惜しいところまでは来てると思うが、EOFはもうちょっと別の扱い方をした方がよさそう

erukitierukiti

素直に read を実装するとかした方がよさそう

EOFはことさらめんどくさい

erukitierukiti

地味にめんどいのが i++ のような ++ がだめってところ。まぁparser 泣かせだから仕方ないんだろうけど

erukitierukiti

文字列扱うのめんどくない?????
better C と言われるとそうなんだろうけど

erukitierukiti

なんか、std.mem.xxx が乱発されてるコードさすがに読みづらいぞ

erukitierukiti

Zig コンストラクタの作り方が特に決まってないので Hoge.new() もあれば Hoge.init() もあるのは、よろしくないよなぁ

erukitierukiti

https://ziglang.org/documentation/master/#intCast

    const a: u16 = 0xabcd;
    print("{}", .{@intCast(u8, a)});

panic: integer cast truncated bits

ふむ。

    const a: u16 = 0xabcd;
    print("{}", .{@intCast(u8, a & 0xff)});

こっちはOK。

かしこすぎない?

erukitierukiti
    const a: u16 = 0xabcd;
    const b: u8 = a;
    print("{}", .{b});

error: integer value 43981 cannot be coerced to type 'u8'

    const a: u16 = 0xabcd;
    const b: u8 = a & 0xff;
    print("{}", .{b});

こっちはOK。かしこすぎない?

というかこれでも同じことできるなら @intCast の使い道が分からなくなった。いらんくない?

erukitierukiti

nrdmn/awesome-zig にあるやつを片っ端から見たりいじったり実験してるんだけど、動かないモノも割とカジュアルにあるっぽいな。メンテナンスされ続けてるとは限らないのか

erukitierukiti

Zig は

const std = @import("std");
const print = std.debug.print;

test {
    const a: u16 = 0xabcd;
    const b: u8 = a & 0xff;
    print("{}", .{b});
}

みたいなファイルを用意して zig test hoge.zig ってやれば簡単に test が走るところ