Zig
最初から付けておけば良かったと思いつつ、まぁいいやってことで書き始める。
zig run hoge.zig
とか zig test hoge.zig
ができるのほんと素晴らしい。
VSCode の拡張は色々足りない感じだけどまぁ仕方なさそう。
ひたすら https://ziglang.org/documentation/0.9.1/ を読んでいる。
C言語の演算子は、マシン語・アセンブリに比べて弱いけど、Zig はそこらへんが拡充されてる分Cよりもチューニングの余地がありそう。
.{hoge}
ってなんじゃい?と思ったが、どうやら無名 struct 的なやつかな。
struct 周りが読んでてなんじゃこりゃ感あふれる。
型定義が const Type = struct { x: u32 }; みたいになってるのなんか納得いかない。なんで const
なの????
In Zig, types are first-class citizens.
ふーむ
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
ならワンチャンって思ったけどだめっぽい
switch は式だし、range 使えるし、union を capture できるし素晴らしや
while (eventuallyNullSequence()) |value| {
sum1 += value;
}
うわ、この発想はなかった。なるほど????
var v: u32 = undefined
が通るの納得いかねぇ
var v: ?u32 = undefined
なら分かるんだけど
というか undefined
と null
の両方があるのなんでなの
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...
表記はできないのが少し残念
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
がだめなんだろうなぁとは思いつつ
とりあえずstr型を用意してみる。
const str = []const u8;
pub fn EndOfFile(source: str) u32 {
return if (source.len == 0) 1 else 0;
}
で str
をいじりまくってみるかな
-
[]T - pointer to runtime-known number of items.
-
runtime-known
ほう。これってcomptime
ってことよね
-
[*]T - many-item pointer to unknown number of items.
unknown にはなるけど、これは source.len
がとれなくなる。まぁそりゃそうか
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'
ファッ!?
なんか単純なC言語脳でもだめなんかな
やりたいことは、引数に index も渡せば問題なく実現できるんだろうけどさぁ
ハー。ぽいんた分かんねぇ(この言葉言うことになる時代が来るとは思わなかった)
こちらでいかがでしょうか
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);
}
Zig だとポインタを足したり引いたりせず、slice 使う方が望ましいのかなー
うーん、分からん。
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はもうちょっと別の扱い方をした方がよさそう
素直に read を実装するとかした方がよさそう
EOFはことさらめんどくさい
地味にめんどいのが i++
のような ++
がだめってところ。まぁparser 泣かせだから仕方ないんだろうけど
とりあえずもう一度公式を読んでみるかなー
Zig コンストラクタの作り方が特に決まってないので Hoge.new()
もあれば Hoge.init()
もあるのは、よろしくないよなぁ
const a: u16 = 0xabcd;
print("{}", .{@intCast(u8, a)});
panic: integer cast truncated bits
ふむ。
const a: u16 = 0xabcd;
print("{}", .{@intCast(u8, a & 0xff)});
こっちはOK。
かしこすぎない?
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
の使い道が分からなくなった。いらんくない?
nrdmn/awesome-zig にあるやつを片っ端から見たりいじったり実験してるんだけど、動かないモノも割とカジュアルにあるっぽいな。メンテナンスされ続けてるとは限らないのか
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 が走るところ