Unknown state [2025-08-04]
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -0,0 +1,2 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
|
1
loader/.envrc
Normal file
1
loader/.envrc
Normal file
@@ -0,0 +1 @@
|
||||
PATH_add zig-out/bin
|
47
loader/main.zig
Normal file
47
loader/main.zig
Normal file
@@ -0,0 +1,47 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Hook = *const fn (i32, i32) callconv(.c) i32;
|
||||
|
||||
pub fn main() !void {
|
||||
std.log.debug("setting up watches...", .{});
|
||||
|
||||
var event: std.os.linux.inotify_event = undefined;
|
||||
const event_buf = std.mem.asBytes(&event);
|
||||
|
||||
const fd: i32 = @intCast(std.os.linux.inotify_init1(std.os.linux.IN.NONBLOCK));
|
||||
defer _ = std.os.linux.close(fd);
|
||||
|
||||
_ = std.os.linux.inotify_add_watch(fd, "./libroot.so", std.os.linux.IN.MODIFY);
|
||||
|
||||
std.log.debug("loading dylib...", .{});
|
||||
|
||||
var lib = try std.DynLib.open("./libroot.so");
|
||||
defer lib.close();
|
||||
|
||||
var hook: Hook = lib.lookup(Hook, "hook").?;
|
||||
|
||||
var polls = [_]std.os.linux.pollfd{
|
||||
.{ .fd = fd, .events = std.os.linux.POLL.IN, .revents = 0 },
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const eps = std.os.linux.poll(&polls, polls.len, 0);
|
||||
std.log.debug("eps: {d}", .{eps});
|
||||
if (eps > 0) {
|
||||
std.log.debug("event!", .{});
|
||||
while (std.os.linux.read(fd, event_buf.ptr, event_buf.len) > 0) {
|
||||
std.log.debug("consume.", .{});
|
||||
}
|
||||
std.log.debug("reloading.", .{});
|
||||
lib.close();
|
||||
lib = try std.DynLib.open("./libroot.so");
|
||||
hook = lib.lookup(Hook, "hook").?;
|
||||
}
|
||||
|
||||
std.log.debug("update", .{});
|
||||
const res = hook(5, 7);
|
||||
std.log.debug("hook(5, 7) = {d}", .{res});
|
||||
|
||||
std.time.sleep(std.time.ns_per_s);
|
||||
}
|
||||
}
|
6
loader/root.zig
Normal file
6
loader/root.zig
Normal file
@@ -0,0 +1,6 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub export fn hook(a: i32, b: i32) i32 {
|
||||
return a * b;
|
||||
}
|
||||
|
5
loader/sample.txt
Normal file
5
loader/sample.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
content
|
||||
|
||||
|
||||
|
||||
|
4
loader/test.zig
Normal file
4
loader/test.zig
Normal file
@@ -0,0 +1,4 @@
|
||||
const std = @import("std");
|
||||
comptime{
|
||||
@compileLog(std.math.maxInt(u64));
|
||||
}
|
41
loader/watch.zig
Normal file
41
loader/watch.zig
Normal file
@@ -0,0 +1,41 @@
|
||||
const std = @import("std");
|
||||
const linux = std.os.linux;
|
||||
|
||||
pub fn main() !void {
|
||||
std.log.debug("setting up watches.", .{});
|
||||
|
||||
var event: linux.inotify_event = undefined;
|
||||
const event_buf = std.mem.asBytes(&event);
|
||||
|
||||
const fd: i32 = @intCast(linux.inotify_init1(linux.IN.NONBLOCK));
|
||||
defer _ = linux.close(fd);
|
||||
|
||||
const wd: i32 = @intCast(linux.inotify_add_watch(
|
||||
fd,
|
||||
"sample.txt",
|
||||
linux.IN.MODIFY | linux.IN.CLOSE_WRITE,
|
||||
));
|
||||
defer _ = linux.inotify_rm_watch(fd, wd);
|
||||
|
||||
var fds = [_]linux.pollfd{
|
||||
.{ .fd = fd, .events = linux.POLL.IN, .revents = 0 },
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const k = linux.poll(&fds, fds.len, 1000);
|
||||
std.log.debug("poll -> {d}", .{k});
|
||||
|
||||
if (k > 0) {
|
||||
while (true) {
|
||||
const n: isize = @bitCast(linux.read(
|
||||
fd,
|
||||
event_buf.ptr,
|
||||
event_buf.len,
|
||||
));
|
||||
std.log.debug("read -> {d}", .{n});
|
||||
std.time.sleep(500 * std.time.ns_per_ms);
|
||||
if (n < 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
md/.tool-versions
Normal file
1
md/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
zig 0.15.0-dev.905+edf785db0
|
13601
md/CommonMark Spec.html
Normal file
13601
md/CommonMark Spec.html
Normal file
File diff suppressed because it is too large
Load Diff
52
md/build.zig
Normal file
52
md/build.zig
Normal file
@@ -0,0 +1,52 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const mod = b.addModule("md", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "md",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "md", .module = mod },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
const exe_tests = b.addTest(.{
|
||||
.root_module = exe.root_module,
|
||||
});
|
||||
|
||||
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
test_step.dependOn(&run_exe_tests.step);
|
||||
}
|
81
md/build.zig.zon
Normal file
81
md/build.zig.zon
Normal file
@@ -0,0 +1,81 @@
|
||||
.{
|
||||
// This is the default name used by packages depending on this one. For
|
||||
// example, when a user runs `zig fetch --save <url>`, this field is used
|
||||
// as the key in the `dependencies` table. Although the user can choose a
|
||||
// different name, most users will stick with this provided value.
|
||||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = .md,
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
// Together with name, this represents a globally unique package
|
||||
// identifier. This field is generated by the Zig toolchain when the
|
||||
// package is first created, and then *never changes*. This allows
|
||||
// unambiguous detection of one package being an updated version of
|
||||
// another.
|
||||
//
|
||||
// When forking a Zig project, this id should be regenerated (delete the
|
||||
// field and run `zig build`) if the upstream project is still maintained.
|
||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||
// original project's identity. Thus it is recommended to leave the comment
|
||||
// on the following line intact, so that it shows up in code reviews that
|
||||
// modify the field.
|
||||
.fingerprint = 0xdb55a2544fdebaae, // Changing this has security and trust implications.
|
||||
// Tracks the earliest Zig version that the package considers to be a
|
||||
// supported use case.
|
||||
.minimum_zig_version = "0.15.0-dev.905+edf785db0",
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
||||
// // which will prevent zig from using it.
|
||||
// .url = "https://example.com/foo.tar.gz",
|
||||
//
|
||||
// // This is computed from the file contents of the directory of files that is
|
||||
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||
// // `paths`.
|
||||
// //
|
||||
// // This field is the source of truth; packages do not come from a `url`; they
|
||||
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||
// // obtain a package matching this `hash`.
|
||||
// //
|
||||
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||
// .hash = "...",
|
||||
//
|
||||
// // When this is provided, the package is found in a directory relative to the
|
||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||
// // computed. This field and `url` are mutually exclusive.
|
||||
// .path = "foo",
|
||||
//
|
||||
// // When this is set to `true`, a package is declared to be lazily
|
||||
// // fetched. This makes the dependency only get fetched if it is
|
||||
// // actually used.
|
||||
// .lazy = false,
|
||||
//},
|
||||
},
|
||||
// Specifies the set of files and directories that are included in this package.
|
||||
// Only files and directories listed here are included in the `hash` that
|
||||
// is computed for this package. Only files listed here will remain on disk
|
||||
// when using the zig package manager. As a rule of thumb, one should list
|
||||
// files required for compilation plus any license(s).
|
||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||
// the build root itself.
|
||||
// A directory listed here means that all files within, recursively, are included.
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
// For example...
|
||||
//"LICENSE",
|
||||
//"README.md",
|
||||
},
|
||||
}
|
194
md/python.md
Normal file
194
md/python.md
Normal file
@@ -0,0 +1,194 @@
|
||||
Title: Python from Scratch
|
||||
Date: 2025-02-01
|
||||
Summary: Building up the Python Data Model from scratch.
|
||||
|
||||
* ** * ** * ** * ** * ** * ** * ** * ** * ** * **
|
||||
|
||||
# Learning to Read
|
||||
|
||||
---
|
||||
|
||||
**First:** A Python program is made up of _tokens_; you can think of these as "
|
||||
words". Some examples of tokens:
|
||||
|
||||
- `"hello world"`
|
||||
- `6`
|
||||
- `(`
|
||||
- `while`
|
||||
- `print`
|
||||
|
||||
Generally there are four types of token in Python, although in practice the
|
||||
lines between them get blurred a little bit.
|
||||
|
||||
- _Literals_ literally represent some value. `"hello world"` and `6` and `4.2`
|
||||
are examples of such literals; the first represents some text and the others
|
||||
represent numbers. This is _literal_ as opposed to some indirect
|
||||
representation like `4 + 2` or `"hello" + " " + "world"`.
|
||||
|
||||
- _Operators_ include things like math operators `+`, `-`, `*`, but also things
|
||||
like the function call operator `( )`, boolean operators `and`, and myriad
|
||||
other operators. [There's a comprehensive list here][expressions] but beware -
|
||||
there's a lot and some of them are pretty technical. The main point is that
|
||||
`( )` and `+` are the same _kind of thing_ as far as the Python interpreter is
|
||||
concerned.
|
||||
|
||||
- _Keywords_ are special directives that tell Python how to behave. This
|
||||
includes things like `if` and `def` and `while`. Technically, operators are
|
||||
also keywords (for example `and` is a keyword) but that's not super relevant
|
||||
here.
|
||||
|
||||
- ___Names___ are the last - and most important - kind of token. `print` is a
|
||||
name. Variable names are names. Function names are names. Class names are
|
||||
names. Module names are names. In all cases, a name represents some _thing_,
|
||||
and Python can fetch that thing if given its name.
|
||||
|
||||
[expressions]: https://docs.python.org/3/reference/expressions.html
|
||||
|
||||
So if I give Python this code:
|
||||
|
||||
```py
|
||||
x = "world"
|
||||
print("hello " + x)
|
||||
```
|
||||
|
||||
You should first identify the tokens:
|
||||
|
||||
- _Name_ `x`
|
||||
- _Operator_ `=`
|
||||
- _Literal_ `"world"`
|
||||
- _Name_ `print`
|
||||
- _Operator_ `( )`
|
||||
- _Literal_ `"hello "`
|
||||
- _Operator_ `+`
|
||||
- _Name_ `x`
|
||||
|
||||
The first line of code binds `"world"` to the name `x`.
|
||||
|
||||
The expression `"hello " + x` looks up the value named by `x` and concatenates
|
||||
it with the literal value `"hello "`. This produces the string `"hello world"`.
|
||||
|
||||
The expression `print( ... )` looks up the value - the function - named by
|
||||
`print` and uses the `( )` operator to call it with the string `"hello world"`.
|
||||
|
||||
To be crystal clear: `x` and `print` _are the same kind of token_, it's just
|
||||
that their named values have different types. One is a string, the other a
|
||||
function. The string can be _operated on_ with the `+` operator, and the
|
||||
function can be _operated on_ with the `( )` operator.
|
||||
|
||||
It is valid to write `print(print)`; here we are looking up the name `print`,
|
||||
and passing that value to the function named by `print`. This should be no more
|
||||
or less surprising than being able to write `x + x` or `5 * 4`.
|
||||
|
||||
# Namespaces
|
||||
|
||||
**First-and-a-half:** A _namespace_ is a collection of names.
|
||||
|
||||
You might also hear this called a "scope". This is the reason I say "maybe three
|
||||
or four, depending how you count"; this is really part of that fundamental idea
|
||||
of a _name_, but I'll list it separately to be extra clear.
|
||||
|
||||
There are some special structures in Python that introduce new namespaces. Each
|
||||
_module_ has a "global" namespace; these are names that can be referenced
|
||||
anywhere in a given file or script. Each _function_ has a "local" namespace;
|
||||
these are names that can only be accessed within the function.
|
||||
|
||||
For example:
|
||||
|
||||
```py
|
||||
x = "eggs"
|
||||
|
||||
|
||||
def spam():
|
||||
y = "ham"
|
||||
|
||||
# I can print(x) here.
|
||||
|
||||
# But I cannot print(y) here.
|
||||
```
|
||||
|
||||
Objects also have namespaces. Names on objects are called "attributes", and they
|
||||
may be simple values or functions, just how regular names might be simple
|
||||
values (`x`, `y`) or functions (`print`, `spam`). You access attributes with the
|
||||
`.` operator.
|
||||
|
||||
```py
|
||||
obj = range(10)
|
||||
print(
|
||||
obj.stop) # find the value named by `obj`, then find the value named by `stop`. 10.
|
||||
```
|
||||
|
||||
Finally, there is the built-in namespace. These are names that are accessible
|
||||
always, from anywhere, by default. Names like `print` and `range` are defined
|
||||
here. [Here's a comprehensive list of built-in names](https://docs.python.org/3/library/functions.html).
|
||||
|
||||
# Strings
|
||||
|
||||
**Second:** you asked about characters and letters, so you may appreciate some
|
||||
background on strings.
|
||||
|
||||
A _string_ is a sequence of characters. A _character_ is simply a number to
|
||||
which we, by convention, assign some meaning. For example, by convention, we've
|
||||
all agreed that the number `74` means `J`. This convention is called an
|
||||
_encoding_. The default encoding is called UTF-8 and is specified by a committee
|
||||
called the _Unicode Consortium_. This encoding includes characters from many
|
||||
current and ancient languages, various symbols and typographical marks, emojis,
|
||||
flags, etc. The important thing to remember is each one of these things, really,
|
||||
is just an integer. And all our devices just agree that when they see a given
|
||||
integer they will look up the appropriate symbol in an appropriate font.
|
||||
|
||||
You can switch between the string representation and the numerical
|
||||
representation with the `encode` and `decode` methods on strings. Really, these
|
||||
are the same, you're just telling Python to tell your console to draw them
|
||||
differently.
|
||||
|
||||
```py
|
||||
>> > list('Fizz'.encode())
|
||||
[70, 105, 122, 122]
|
||||
>> > bytes([66, 117, 122, 122]).decode()
|
||||
'Buzz'
|
||||
```
|
||||
|
||||
For continuity: `list`, `encode`, `decode`, and `bytes` are all names. `( )`,
|
||||
`[ ]`, `,`, and `.` are all operators. The numbers and `'Fizz'` are literals.
|
||||
|
||||
† Technically, `[66, 117, 122, 122]` in its entirety is a literal - `,` is a
|
||||
keyword, not an operator - but that's neither here nor there for these purposes.
|
||||
|
||||
‡ The symbol `†` is number 8224 and the symbol `‡` is number 8225.
|
||||
|
||||
# Names
|
||||
|
||||
**Second-and-a-half:** names are strings.
|
||||
|
||||
Names are just strings, and namespaces are just `dict`. You can access them with
|
||||
`locals()` and `globals()`, although in practice you almost never need to do
|
||||
this directly. It's better to just use the name itself.
|
||||
|
||||
```py
|
||||
import pprint
|
||||
|
||||
x = range(10)
|
||||
function = print
|
||||
pprint.pprint(globals())
|
||||
```
|
||||
|
||||
This outputs:
|
||||
|
||||
```
|
||||
{'__annotations__': {},
|
||||
'__builtins__': <module 'builtins' (built-in)>,
|
||||
'__cached__': None,
|
||||
'__doc__': None,
|
||||
'__file__': '<stdin>',
|
||||
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
|
||||
'__name__': '__main__',
|
||||
'__package__': None,
|
||||
'__spec__': None,
|
||||
'function': <built-in function print>,
|
||||
'pprint': <module 'pprint' from 'python3.12/pprint.py'>,
|
||||
'x': range(0, 10)}
|
||||
```
|
||||
|
||||
For continuity: `import pprint` binds the name `pprint` to the module
|
||||
`pprint.py` from the standard library. The line `pprint.pprint( ... )` fetches
|
||||
the function `pprint` from that module, and calls it.
|
9756
md/spec-0.31.2.md
Normal file
9756
md/spec-0.31.2.md
Normal file
File diff suppressed because it is too large
Load Diff
156
md/src/main.zig
Normal file
156
md/src/main.zig
Normal file
@@ -0,0 +1,156 @@
|
||||
//! Markdown Parser.
|
||||
//!
|
||||
//! Leaf blocks
|
||||
//! Thematic Breaks
|
||||
//! ATX headings
|
||||
//! Setext headings
|
||||
//! Indented code blocks
|
||||
//! Fenced code blocks
|
||||
//! HTML blocks
|
||||
//! Link reference definitions
|
||||
//! Paragraphs
|
||||
//! Blank lines
|
||||
//!
|
||||
//! Container blocks
|
||||
//! Block Quotes
|
||||
//! List Items
|
||||
//! Lists
|
||||
//!
|
||||
//! Inlines
|
||||
//! Code spans
|
||||
//! emph, strong
|
||||
//! Links
|
||||
//! Images
|
||||
//! Autolinks
|
||||
//! Raw HTML
|
||||
//! Hard line breaks
|
||||
//! Soft line breaks
|
||||
//! Text
|
||||
|
||||
const BlockType = enum {
|
||||
// Leaf Blocks
|
||||
Break,
|
||||
Heading,
|
||||
Code,
|
||||
HTML,
|
||||
LinkDefinition,
|
||||
Paragraph,
|
||||
Blank,
|
||||
|
||||
// Container Blocks
|
||||
Quote,
|
||||
List,
|
||||
// ListItem,
|
||||
};
|
||||
|
||||
const Block = struct {
|
||||
type: BlockType,
|
||||
text: []const u8,
|
||||
depth: u8 = 0,
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const md = @import("md");
|
||||
|
||||
pub fn render(src: []const u8, alloc: std.mem.Allocator) !void {
|
||||
var it = std.mem.splitScalar(u8, src, '\n');
|
||||
|
||||
var blocks = std.ArrayList(Block).init(alloc);
|
||||
defer blocks.deinit();
|
||||
|
||||
line_search: while (it.next()) |line| {
|
||||
// early check for blank line
|
||||
for (line) |ch| {
|
||||
if (!std.ascii.isWhitespace(ch)) break;
|
||||
} else {
|
||||
try blocks.append(.{ .type = .Blank, .text = line });
|
||||
continue :line_search;
|
||||
}
|
||||
|
||||
// figure out indentation
|
||||
var depth: u8 = 0;
|
||||
var start: u8 = 0;
|
||||
|
||||
for (line) |ch| {
|
||||
switch (ch) {
|
||||
' ' => depth += 1,
|
||||
'\t' => depth += 4,
|
||||
else => break,
|
||||
}
|
||||
start += 1;
|
||||
}
|
||||
// at this point, start will not be line.len because we checked
|
||||
// it is not blank
|
||||
|
||||
if (depth >= 4) {
|
||||
// todo something special has to happen. lists and code blocks.
|
||||
}
|
||||
|
||||
// test thematic break
|
||||
switch (line[start]) {
|
||||
'*', '-', '_' => |kind| {
|
||||
var count: u8 = 0;
|
||||
for (line[start..]) |ch| {
|
||||
if (ch == kind) {
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.ascii.isWhitespace(ch)) continue;
|
||||
|
||||
break;
|
||||
} else {
|
||||
if (count >= 3) {
|
||||
try blocks.append(.{ .type = .Break, .text = line });
|
||||
continue :line_search;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
for (blocks.items) |block| {
|
||||
std.debug.print("{any}\n", .{block});
|
||||
}
|
||||
|
||||
// TODO split into paragraphs, list elements
|
||||
//
|
||||
|
||||
// const State = enum {
|
||||
// Block,
|
||||
// };
|
||||
|
||||
// mach: switch (State.Block) {
|
||||
// .Block => {
|
||||
// continue :mach .Block;
|
||||
// },
|
||||
// }
|
||||
|
||||
// while (it.next()) |line| {}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
const args = try std.process.argsAlloc(alloc);
|
||||
defer std.process.argsFree(alloc, args);
|
||||
|
||||
const buf = try alloc.alloc(u8, 1 << 20);
|
||||
defer alloc.free(buf);
|
||||
|
||||
for (args[1..]) |path| {
|
||||
const file = try std.fs.cwd().openFile(path, .{ .mode = .read_only });
|
||||
defer file.close();
|
||||
const stat = try file.stat();
|
||||
if (stat.size > buf.len)
|
||||
return error.FileTooBig;
|
||||
|
||||
const n = try file.readAll(buf);
|
||||
const content = buf[0..n];
|
||||
|
||||
try render(content, alloc);
|
||||
}
|
||||
}
|
54
md/src/root.zig
Normal file
54
md/src/root.zig
Normal file
@@ -0,0 +1,54 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn parse(src: []const u8, alloc: std.mem.Allocator) ![][]const u8 {
|
||||
var it = std.mem.splitScalar(u8, src, '\n');
|
||||
|
||||
var lines = std.ArrayList([]const u8).init(alloc);
|
||||
|
||||
while (it.next()) |line| {
|
||||
try lines.append(line);
|
||||
}
|
||||
|
||||
return try lines.toOwnedSlice();
|
||||
}
|
||||
|
||||
// pub fn render(src: []const u8, alloc: std.mem.Allocator) ![]const u8 {
|
||||
// // std.mem.tokenizeScalar(comptime T: type, buffer: []const T, delimiter: T)
|
||||
// }
|
||||
|
||||
test "split-lines" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const src =
|
||||
\\hello world!
|
||||
\\
|
||||
\\this is some content!
|
||||
\\
|
||||
;
|
||||
|
||||
const lines = try parse(src, alloc);
|
||||
defer alloc.free(lines);
|
||||
|
||||
const expect: []const []const u8 = &.{
|
||||
"hello world!",
|
||||
"",
|
||||
"this is some content!",
|
||||
"",
|
||||
};
|
||||
|
||||
try std.testing.expectEqualDeep(
|
||||
expect,
|
||||
lines,
|
||||
);
|
||||
}
|
||||
|
||||
test "2.2 Tabs" {
|
||||
// const alloc = std.testing.allocator;
|
||||
//
|
||||
// const md = "\tfoo\tbaz\t\tbim";
|
||||
|
||||
// const expect = "<pre><code>foo\tbaz\t\tbim</code></pre>";
|
||||
// const actual = try render(md, alloc);
|
||||
// defer alloc.free(actual);
|
||||
// std.testing.expectEqualStrings(expect, actual);
|
||||
}
|
BIN
md/zig-out/bin/md
Executable file
BIN
md/zig-out/bin/md
Executable file
Binary file not shown.
1
statusline/.tool-versions
Normal file
1
statusline/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
zig 0.15.0-dev.905+edf785db0
|
79
statusline/Cpu.zig
Normal file
79
statusline/Cpu.zig
Normal file
@@ -0,0 +1,79 @@
|
||||
const std = @import("std");
|
||||
const Self = @This();
|
||||
|
||||
fh: std.fs.File,
|
||||
buf: []u8,
|
||||
stats: Stats,
|
||||
perc: u8 = 0,
|
||||
|
||||
pub fn init(self: *Self, alloc: std.mem.Allocator) !void {
|
||||
self.fh = try std.fs.openFileAbsolute("/proc/stat", .{ .mode = .read_only });
|
||||
errdefer self.fh.close();
|
||||
|
||||
self.buf = try alloc.alloc(u8, 256);
|
||||
errdefer alloc.free(self.buf);
|
||||
|
||||
self.stats = try self.get();
|
||||
}
|
||||
|
||||
pub const Stats = struct {
|
||||
all: u32,
|
||||
all_idle: u32,
|
||||
all_active: u32,
|
||||
};
|
||||
|
||||
pub fn update(self: *Self) ![]const u8 {
|
||||
const delta = try self.get_delta();
|
||||
if (delta.all != 0) {
|
||||
self.perc = @intCast(@divFloor(1 + 2 * 100 * delta.all_active, 2 * delta.all));
|
||||
}
|
||||
return try std.fmt.bufPrint(self.buf, "cpu: {d:0>2}%", .{self.perc});
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self, alloc: std.mem.Allocator) void {
|
||||
self.fh.close();
|
||||
alloc.free(self.buf);
|
||||
}
|
||||
|
||||
fn get(self: Self) !Stats {
|
||||
try self.fh.seekTo(0);
|
||||
const line = try self.fh.reader().readUntilDelimiter(self.buf, '\n');
|
||||
|
||||
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
|
||||
_ = tokens.next(); // skip "cpu"
|
||||
|
||||
var raw: struct {
|
||||
user: u32,
|
||||
nice: u32,
|
||||
system: u32,
|
||||
idle: u32,
|
||||
iowait: u32,
|
||||
irq: u32,
|
||||
softirq: u32,
|
||||
steal: u32,
|
||||
guest: u32,
|
||||
guest_nice: u32,
|
||||
} = undefined;
|
||||
|
||||
inline for (std.meta.fields(@TypeOf(raw))) |f| {
|
||||
const token = tokens.next() orelse return error.MissingStat;
|
||||
@field(raw, f.name) = try std.fmt.parseInt(f.type, token, 10);
|
||||
}
|
||||
|
||||
const idle = raw.idle + raw.iowait;
|
||||
const active = raw.user + raw.nice + raw.system + raw.irq + raw.softirq + raw.steal;
|
||||
|
||||
return .{ .all = idle + active, .all_idle = idle, .all_active = active };
|
||||
}
|
||||
|
||||
fn get_delta(self: *Self) !Stats {
|
||||
const prev = self.stats;
|
||||
const next = try self.get();
|
||||
self.stats = next;
|
||||
|
||||
return .{
|
||||
.all = next.all - prev.all,
|
||||
.all_idle = next.all_idle - prev.all_idle,
|
||||
.all_active = next.all_active - prev.all_active,
|
||||
};
|
||||
}
|
58
statusline/Mem.zig
Normal file
58
statusline/Mem.zig
Normal file
@@ -0,0 +1,58 @@
|
||||
const std = @import("std");
|
||||
const Self = @This();
|
||||
|
||||
fh: std.fs.File,
|
||||
dat: []u8,
|
||||
buf: []u8,
|
||||
perc: u8 = 0,
|
||||
|
||||
pub fn init(self: *Self, alloc: std.mem.Allocator) !void {
|
||||
self.fh = try std.fs.openFileAbsolute("/proc/meminfo", .{ .mode = .read_only });
|
||||
errdefer self.fh.close();
|
||||
|
||||
self.dat = try alloc.alloc(u8, 256);
|
||||
errdefer alloc.free(self.dat);
|
||||
|
||||
self.buf = try alloc.alloc(u8, 256);
|
||||
errdefer alloc.free(self.buf);
|
||||
}
|
||||
|
||||
pub fn update(self: *Self) ![]const u8 {
|
||||
try self.fh.seekTo(0);
|
||||
var br = std.io.bufferedReader(self.fh.reader());
|
||||
const reader = br.reader();
|
||||
|
||||
var total: u32 = undefined;
|
||||
var free: u32 = undefined;
|
||||
var avail: u32 = undefined;
|
||||
|
||||
for (0..3) |_| {
|
||||
const line = try reader.readUntilDelimiterOrEof(self.dat, '\n');
|
||||
var tokens = std.mem.tokenizeAny(u8, line.?, ": ");
|
||||
|
||||
const label = tokens.next().?;
|
||||
const value = try std.fmt.parseInt(u32, tokens.next().?, 10);
|
||||
|
||||
if (std.mem.eql(u8, label, "MemTotal")) {
|
||||
total = value;
|
||||
} else if (std.mem.eql(u8, label, "MemFree")) {
|
||||
free = value;
|
||||
} else if (std.mem.eql(u8, label, "MemAvailable")) {
|
||||
avail = value;
|
||||
}
|
||||
}
|
||||
|
||||
const perc_used: u8 = @intCast(@divFloor(1 + 2 * 100 * (total - avail), 2 * total));
|
||||
|
||||
return try std.fmt.bufPrint(
|
||||
self.buf,
|
||||
"mem: {d: >2}%",
|
||||
.{perc_used},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self, alloc: std.mem.Allocator) void {
|
||||
self.fh.close();
|
||||
alloc.free(self.dat);
|
||||
alloc.free(self.buf);
|
||||
}
|
29
statusline/Time.zig
Normal file
29
statusline/Time.zig
Normal file
@@ -0,0 +1,29 @@
|
||||
const std = @import("std");
|
||||
const Self = @This();
|
||||
|
||||
const time = @cImport({
|
||||
@cInclude("time.h");
|
||||
});
|
||||
|
||||
buf: []u8,
|
||||
|
||||
pub fn init(self: *Self, alloc: std.mem.Allocator) !void {
|
||||
self.buf = try alloc.alloc(u8, 64);
|
||||
errdefer alloc.free(self.buf);
|
||||
}
|
||||
|
||||
pub fn update(self: *Self) ![]const u8 {
|
||||
const tt: time.time_t = time.time(null);
|
||||
const tp = time.localtime(&tt);
|
||||
const n = time.strftime(
|
||||
self.buf.ptr,
|
||||
self.buf.len,
|
||||
"%c",
|
||||
tp,
|
||||
);
|
||||
return self.buf[0..n];
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
||||
alloc.free(self.buf);
|
||||
}
|
95
statusline/cpu.bak.zig
Normal file
95
statusline/cpu.bak.zig
Normal file
@@ -0,0 +1,95 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const RawStats = struct {
|
||||
user: u32,
|
||||
nice: u32,
|
||||
system: u32,
|
||||
idle: u32,
|
||||
iowait: u32,
|
||||
irq: u32,
|
||||
softirq: u32,
|
||||
steal: u32,
|
||||
guest: u32,
|
||||
guest_nice: u32,
|
||||
};
|
||||
|
||||
pub const Stats = struct {
|
||||
all: u32,
|
||||
all_idle: u32,
|
||||
all_active: u32,
|
||||
|
||||
pub fn active_ratio(self: Stats) f32 {
|
||||
return @as(f32, @floatFromInt(self.all_active)) / @as(f32, @floatFromInt(self.all));
|
||||
}
|
||||
|
||||
pub fn active_perc(self: Stats) u8 {
|
||||
return @intCast(100 * self.all_active / self.all);
|
||||
}
|
||||
};
|
||||
|
||||
fh: std.fs.File,
|
||||
buf: []u8,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !Self {
|
||||
const fh = try std.fs.openFileAbsolute("/proc/stat", .{ .mode = .read_only });
|
||||
errdefer fh.close();
|
||||
|
||||
const buf = try alloc.alloc(u8, 512);
|
||||
errdefer alloc.free(buf);
|
||||
|
||||
return .{ .fh = fh, .buf = buf };
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self, alloc: std.mem.Allocator) void {
|
||||
self.fh.close();
|
||||
alloc.free(self.buf);
|
||||
}
|
||||
|
||||
pub fn get(self: Self) !Stats {
|
||||
try self.fh.seekTo(0);
|
||||
const line = try self.fh.reader().readUntilDelimiter(self.buf, '\n');
|
||||
|
||||
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
|
||||
_ = tokens.next(); // skip "cpu"
|
||||
|
||||
var raw: RawStats = undefined;
|
||||
|
||||
inline for (std.meta.fields(RawStats)) |f| {
|
||||
const token = tokens.next() orelse return error.MissingStat;
|
||||
@field(raw, f.name) = try std.fmt.parseInt(f.type, token, 10);
|
||||
}
|
||||
|
||||
const idle = raw.idle + raw.iowait;
|
||||
const active = raw.user + raw.nice + raw.system + raw.irq + raw.softirq + raw.steal;
|
||||
|
||||
return .{ .all = idle + active, .all_idle = idle, .all_active = active };
|
||||
}
|
||||
|
||||
pub fn get_delta(self: Self, prev: *Stats) !Stats {
|
||||
const next = try self.get();
|
||||
const delta: Stats = .{
|
||||
.all = next.all - prev.all,
|
||||
.all_idle = next.all_idle - prev.all_idle,
|
||||
.all_active = next.all_active - prev.all_active,
|
||||
};
|
||||
prev.* = next;
|
||||
return delta;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
const monitor = try Self.init(alloc);
|
||||
defer monitor.deinit(alloc);
|
||||
|
||||
var stats = try monitor.get();
|
||||
std.time.sleep(std.time.ns_per_s);
|
||||
const delta = try monitor.get_delta(&stats);
|
||||
|
||||
std.debug.print("{any}\n", .{delta});
|
||||
std.debug.print("{d:.6} {d}\n", .{ delta.active_ratio(), delta.active_perc() });
|
||||
}
|
41
statusline/main.zig
Normal file
41
statusline/main.zig
Normal file
@@ -0,0 +1,41 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Frequency = 500;
|
||||
const Mods = .{
|
||||
@import("Mem.zig"),
|
||||
@import("Cpu.zig"),
|
||||
@import("Time.zig"),
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
var mods: std.meta.Tuple(&Mods) = undefined;
|
||||
inline for (&mods) |*mod| {
|
||||
try mod.init(alloc);
|
||||
}
|
||||
defer {
|
||||
inline for (&mods) |*mod| {
|
||||
mod.deinit(alloc);
|
||||
}
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
const buffer = try alloc.alloc(u8, 512);
|
||||
defer alloc.free(buffer);
|
||||
|
||||
var outputs: [Mods.len][]const u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
std.time.sleep(Frequency * std.time.ns_per_ms);
|
||||
|
||||
inline for (&mods, &outputs) |*mod, *out| {
|
||||
out.* = try mod.update();
|
||||
}
|
||||
|
||||
const output = try std.fmt.bufPrint(buffer, "{s}\n", .{outputs});
|
||||
try stdout.writeAll(output);
|
||||
}
|
||||
}
|
80
statusline/modules-wip.zig
Normal file
80
statusline/modules-wip.zig
Normal file
@@ -0,0 +1,80 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Module = struct {
|
||||
init: *const fn (std.mem.Allocator) *anyopaque,
|
||||
deinit: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
update: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
};
|
||||
|
||||
const Foo = struct {
|
||||
fn init(alloc: std.mem.Allocator) *@This() {
|
||||
return alloc.create(@This()) catch @panic("OOM");
|
||||
}
|
||||
|
||||
fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
alloc.destroy(self);
|
||||
}
|
||||
|
||||
fn update(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
_ = self;
|
||||
_ = alloc;
|
||||
|
||||
std.log.debug("Hello, {s}", .{@typeName(@This())});
|
||||
}
|
||||
};
|
||||
|
||||
const Bar = struct {
|
||||
fn init(alloc: std.mem.Allocator) *@This() {
|
||||
return alloc.create(@This()) catch @panic("OOM");
|
||||
}
|
||||
|
||||
fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
alloc.destroy(self);
|
||||
}
|
||||
|
||||
fn update(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
_ = self;
|
||||
_ = alloc;
|
||||
|
||||
std.log.debug("Hello, {s}", .{@typeName(@This())});
|
||||
}
|
||||
};
|
||||
|
||||
const Modules = .{
|
||||
Foo,
|
||||
Bar,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
// comptime var modules: [Modules.len]Module = undefined;
|
||||
comptime var modules: [Modules.len]type = undefined;
|
||||
|
||||
inline for (Modules, &modules) |Mod, *mod| {
|
||||
mod.* = struct {
|
||||
pub fn init(alloc: std.mem.Allocator) void {
|
||||
}
|
||||
};
|
||||
// mod.* = .{
|
||||
// .init = @ptrCast(&Mod.init),
|
||||
// .deinit = @ptrCast(&Mod.deinit),
|
||||
// .update = @ptrCast(&Mod.update),
|
||||
// };
|
||||
}
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
var states: [Modules.len]*anyopaque = undefined;
|
||||
for (modules, &states) |mod, *state| {
|
||||
state.* = mod.init(alloc);
|
||||
}
|
||||
defer for (modules, states) |mod, state| {
|
||||
mod.deinit(state, alloc);
|
||||
};
|
||||
|
||||
std.log.debug("Performing update...", .{});
|
||||
for (modules, states) |mod, state| {
|
||||
mod.update(state, alloc);
|
||||
}
|
||||
}
|
73
statusline/status.zig
Normal file
73
statusline/status.zig
Normal file
@@ -0,0 +1,73 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Blocks = .{
|
||||
@import("Cpu.zig").block(.{}),
|
||||
@import("Cpu.zig").block(.{}), // just testing a second block
|
||||
};
|
||||
|
||||
const freq_ms = 500;
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
var blocks: std.meta.Tuple(&Blocks) = undefined;
|
||||
var arenas: [Blocks.len]std.heap.ArenaAllocator = undefined;
|
||||
var outputs: [Blocks.len][]const u8 = undefined;
|
||||
|
||||
var combined_arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer combined_arena.deinit();
|
||||
|
||||
inline for (Blocks, &blocks, &arenas) |Block, *block, *arena| {
|
||||
block.* = try Block.init(alloc);
|
||||
arena.* = std.heap.ArenaAllocator.init(alloc);
|
||||
}
|
||||
|
||||
defer inline for (blocks, arenas) |block, arena| {
|
||||
block.deinit(alloc);
|
||||
arena.deinit();
|
||||
};
|
||||
|
||||
std.time.sleep(std.time.ns_per_s);
|
||||
|
||||
inline for (&blocks, &arenas, &outputs) |*block, *arena, *output| {
|
||||
output.* = try block.update(arena.allocator());
|
||||
}
|
||||
|
||||
_ = combined_arena.reset(.retain_capacity);
|
||||
const combined = try std.mem.join(combined_arena.allocator(), " * ", &outputs);
|
||||
try std.io.getStdOut().writeAll(combined);
|
||||
try std.io.getStdOut().writeAll("\n");
|
||||
}
|
||||
|
||||
// pub fn main() !void {
|
||||
// var arenas: [Blocks.len]std.heap.ArenaAllocator = undefined;
|
||||
// inline for (&arenas) |*arena| {
|
||||
// arena.* = std.heap.ArenaAllocator.init(alloc);
|
||||
// }
|
||||
//
|
||||
// var outs: [Blocks.len][]u8 = undefined;
|
||||
//
|
||||
// var buf = std.ArrayList(u8).init(alloc);
|
||||
// defer buf.deinit();
|
||||
// try buf.ensureTotalCapacity(512);
|
||||
//
|
||||
// while (true) {
|
||||
// inline for (&blocks, &arenas, &outs) |*block, *arena, *out| {
|
||||
// arena.reset(.{ .retain_with_limit = 100 });
|
||||
// out.* = try block.update(arena.allocator());
|
||||
// }
|
||||
//
|
||||
// std.mem.join(allocator: Allocator, separator: []const u8, slices: []const []const u8)
|
||||
//
|
||||
// // try buf.resize(0);
|
||||
// // inline for (&blocks) |*block| {
|
||||
// // try block.update();
|
||||
// // try block.print(buf.fixedWriter().any());
|
||||
// // }
|
||||
// // std.debug.print("buf: {s}\n", .{buf.items});
|
||||
// //
|
||||
// // std.time.sleep(freq_ms * std.time.ns_per_ms);
|
||||
// }
|
||||
// }
|
1
zzd/.tool-versions
Normal file
1
zzd/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
zig 0.15.0-dev.905+edf785db0
|
50
zzd/main.zig
Normal file
50
zzd/main.zig
Normal file
@@ -0,0 +1,50 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
var args = std.process.args();
|
||||
defer args.deinit();
|
||||
|
||||
std.debug.assert(args.skip());
|
||||
|
||||
const filename = args.next() orelse {
|
||||
std.debug.print("Invalid usage.\n", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
const file = try cwd.openFileZ(filename, .{
|
||||
.mode = .read_only,
|
||||
.lock = .none,
|
||||
});
|
||||
defer file.close();
|
||||
|
||||
var rb = std.io.bufferedReader(file.reader());
|
||||
const in = rb.reader();
|
||||
|
||||
var stdout = std.io.getStdOut();
|
||||
var bw = std.io.bufferedWriter(stdout.writer());
|
||||
|
||||
const out = bw.writer();
|
||||
|
||||
const N = 16;
|
||||
const B = 2;
|
||||
|
||||
var idx: usize = 0;
|
||||
while (in.readByte() catch null) |b| {
|
||||
idx += 1;
|
||||
try out.print("{x:0>2}", .{b});
|
||||
if (idx % N == 0) {
|
||||
try out.print("\n", .{});
|
||||
idx = 0;
|
||||
} else if (idx % B == 0) {
|
||||
try out.print(" ", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (idx != 0) {
|
||||
try out.print("\n", .{});
|
||||
}
|
||||
try out.writeByteNTimes('=', 2 * N + N / B - 1);
|
||||
|
||||
try bw.flush();
|
||||
}
|
Reference in New Issue
Block a user