Skip to documentation
SLOP

tiny.chant.preprocess.embed

Reference tiny.chant preprocess embed

Defined in preprocess.

API (4)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callspreprocessembed
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallspreprocessrunprivate sourcelib.chant.src.preprocess.embed.ExpanderexpandEmbedpreprocess.embed.ExpanderexpandOutput
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallspreprocessrunprivate sourcelib.chant.src.preprocess.embedsourceDirectorypreprocess.embed.Expanderinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallspreprocessrunprivate sourcelib.chant.src.preprocess.embed.ExpanderprepareSourceprivate sourcelib.chant.src.preprocess.embedappendLineMarkerpreprocess.embed.Expanderprepare
Static calls · unresolved targets: 0 · external targets: 2.

Source: lib/chant/src/preprocess/embed.zig

zig
const std = @import("std");const sys = @import("sys");const types = @import("types.zig");const Error = types.Error;const Options = types.Options;const max_resource_bytes = 16 * 1024 * 1024;const max_include_depth = 64;const sentinel_prefix = "__CHANT_EMBED_";const Status = enum(u8) {    not_found = 0,    found = 1,    empty = 2,};const Params = struct {    limit: ?usize = null,    prefix: ?[]const u8 = null,    suffix: ?[]const u8 = null,    if_empty: ?[]const u8 = null,};const Request = struct {    name: []const u8,    quoted: bool,    params: Params,};const Embed = struct {    raw: []const u8,    source_dir: []const u8,};const Directive = struct {    name: []const u8,    argument: []const u8,};const Macro = struct {    name: []const u8,    replacement: []const u8,    params: []const []const u8 = &.{},    function_like: bool = false,    variadic: bool = false,};const Include = struct {    name: []const u8,    quoted: bool,};const Included = struct {    path: []const u8,    dir: []const u8,    source: []const u8,};const Condition = struct {    parent_active: bool,    active: bool,    branch_taken: bool,};pub const Expander = struct {    arena: std.mem.Allocator,    source_path: []const u8,    source_dir: []const u8,    options: Options,    embeds: std.ArrayListUnmanaged(Embed) = .empty,    macros: std.ArrayListUnmanaged(Macro) = .empty,    conditions: std.ArrayListUnmanaged(Condition) = .empty,    changed: bool = false,    pub fn init(arena: std.mem.Allocator, source_path: []const u8, options: Options) Error!Expander {        return .{            .arena = arena,            .source_path = source_path,            .source_dir = sourceDirectory(arena, source_path) catch return error.OutOfMemory,            .options = options,        };    }    pub fn prepare(self: *Expander, source: []const u8) Error![]const u8 {        const body = try self.prepareSource(self.source_path, self.source_dir, source, 0);        if (!self.changed) return source;        var out = std.ArrayListUnmanaged(u8).empty;        try out.appendSlice(self.arena,            \\#ifndef __STDC_EMBED_NOT_FOUND__            \\#define __STDC_EMBED_NOT_FOUND__ 0            \\#endif            \\#ifndef __STDC_EMBED_FOUND__            \\#define __STDC_EMBED_FOUND__ 1            \\#endif            \\#ifndef __STDC_EMBED_EMPTY__            \\#define __STDC_EMBED_EMPTY__ 2            \\#endif            \\        );        try appendLineMarker(self.arena, &out, self.source_path);        try out.appendSlice(self.arena, body);        return out.toOwnedSlice(self.arena);    }    fn prepareSource(self: *Expander, source_path: []const u8, source_dir: []const u8, source: []const u8, depth: usize) Error![]const u8 {        if (depth > max_include_depth) return error.PreprocessFailed;        var body = std.ArrayListUnmanaged(u8).empty;        var index: usize = 0;        while (index < source.len) {            const start = index;            while (index < source.len and source[index] != '\n') : (index += 1) {}            const line = source[start..index];            const has_newline = index < source.len;            if (has_newline) index += 1;            var appended = false;            if (parseDirective(line)) |direct| {                if (try self.appendConditionalDirective(&body, line, direct, source_dir)) {                    appended = true;                } else if (!self.currentActive()) {                    try body.appendSlice(self.arena, line);                    appended = true;                } else {                    if (std.mem.eql(u8, direct.name, "define")) {                        try self.recordDefine(direct.argument);                    } else if (std.mem.eql(u8, direct.name, "undef")) {                        self.recordUndef(direct.argument);                    } else if (std.mem.eql(u8, direct.name, "include")) {                        if (try self.readInclude(direct.argument, source_dir)) |included| {                            const prepared = try self.prepareSource(included.path, included.dir, included.source, depth + 1);                            try self.appendPreparedInclude(&body, included.path, source_path, prepared);                            appended = true;                        }                    }                    if (!appended and std.mem.eql(u8, direct.name, "embed")) {                        const id = self.embeds.items.len;                        const expanded = try self.expandMacros(direct.argument);                        try self.embeds.append(self.arena, .{ .raw = expanded, .source_dir = source_dir });                        self.changed = true;                        try appendPrint(self.arena, &body, "{s}{d}__", .{ sentinel_prefix, id });                        appended = true;                    }                    if (!appended and try rewriteHasEmbedDefined(self, &body, direct)) appended = true;                }            }            if (!appended) {                if (!try self.appendHasEmbedExpressions(&body, line, source_dir)) {                    try body.appendSlice(self.arena, line);                }            }            if (has_newline) try body.append(self.arena, '\n');        }        return body.toOwnedSlice(self.arena);    }    fn readInclude(self: *Expander, raw: []const u8, source_dir: []const u8) Error!?Included {        const expanded = try self.expandMacros(raw);        const include = parseInclude(expanded) orelse return null;        const path = self.findInclude(include, source_dir) orelse return null;        const included: ?[]const u8 = sys.fs.readFileAlloc(self.arena, path, max_resource_bytes) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            else => null,        };        const source = included orelse return null;        const dir = sourceDirectory(self.arena, path) catch return error.OutOfMemory;        return .{ .path = path, .dir = dir, .source = source };    }    fn appendPreparedInclude(self: *Expander, out: *std.ArrayListUnmanaged(u8), path: []const u8, source_path: []const u8, prepared: []const u8) Error!void {        self.changed = true;        try appendLineMarker(self.arena, out, path);        try out.appendSlice(self.arena, prepared);        if (prepared.len == 0 or prepared[prepared.len - 1] != '\n') try out.append(self.arena, '\n');        try appendLineMarker(self.arena, out, source_path);    }    fn appendConditionalDirective(self: *Expander, out: *std.ArrayListUnmanaged(u8), line: []const u8, direct: Directive, source_dir: []const u8) Error!bool {        if (std.mem.eql(u8, direct.name, "if")) {            const parent_active = self.currentActive();            const condition = try self.evalCondition(direct.argument, source_dir);            try self.conditions.append(self.arena, .{                .parent_active = parent_active,                .active = parent_active and condition,                .branch_taken = condition,            });        } else if (std.mem.eql(u8, direct.name, "ifdef")) {            const parent_active = self.currentActive();            const condition = self.isMacroDefined(std.mem.trim(u8, direct.argument, " \t\r"));            try self.conditions.append(self.arena, .{                .parent_active = parent_active,                .active = parent_active and condition,                .branch_taken = condition,            });        } else if (std.mem.eql(u8, direct.name, "ifndef")) {            const parent_active = self.currentActive();            const condition = !self.isMacroDefined(std.mem.trim(u8, direct.argument, " \t\r"));            try self.conditions.append(self.arena, .{                .parent_active = parent_active,                .active = parent_active and condition,                .branch_taken = condition,            });        } else if (std.mem.eql(u8, direct.name, "elif")) {            if (self.conditions.items.len > 0) {                const condition = try self.evalCondition(direct.argument, source_dir);                const top = &self.conditions.items[self.conditions.items.len - 1];                top.active = top.parent_active and !top.branch_taken and condition;                top.branch_taken = top.branch_taken or condition;            }        } else if (std.mem.eql(u8, direct.name, "elifdef")) {            if (self.conditions.items.len > 0) {                const condition = self.isMacroDefined(std.mem.trim(u8, direct.argument, " \t\r"));                const top = &self.conditions.items[self.conditions.items.len - 1];                top.active = top.parent_active and !top.branch_taken and condition;                top.branch_taken = top.branch_taken or condition;            }        } else if (std.mem.eql(u8, direct.name, "elifndef")) {            if (self.conditions.items.len > 0) {                const condition = !self.isMacroDefined(std.mem.trim(u8, direct.argument, " \t\r"));                const top = &self.conditions.items[self.conditions.items.len - 1];                top.active = top.parent_active and !top.branch_taken and condition;                top.branch_taken = top.branch_taken or condition;            }        } else if (std.mem.eql(u8, direct.name, "else")) {            if (self.conditions.items.len > 0) {                const top = &self.conditions.items[self.conditions.items.len - 1];                top.active = top.parent_active and !top.branch_taken;                top.branch_taken = true;            }        } else if (std.mem.eql(u8, direct.name, "endif")) {            if (self.conditions.items.len > 0) _ = self.conditions.pop();        } else {            return false;        }        if (try rewriteHasEmbedDefined(self, out, direct)) return true;        if (try self.appendHasEmbedExpressions(out, line, source_dir)) return true;        try out.appendSlice(self.arena, line);        return true;    }    fn currentActive(self: *const Expander) bool {        if (self.conditions.items.len == 0) return true;        return self.conditions.items[self.conditions.items.len - 1].active;    }    fn appendHasEmbedExpressions(self: *Expander, out: *std.ArrayListUnmanaged(u8), line: []const u8, source_dir: []const u8) Error!bool {        var replaced = false;        const expanded = try self.expandHasEmbedExpressions(line, source_dir, &replaced);        if (!replaced) return false;        try out.appendSlice(self.arena, expanded);        self.changed = true;        return true;    }    fn expandHasEmbedExpressions(self: *Expander, line: []const u8, source_dir: []const u8, replaced: *bool) Error![]const u8 {        var index: usize = 0;        var out = std.ArrayListUnmanaged(u8).empty;        while (index < line.len) {            if (line[index] == '"') {                const end = findStringEnd(line, index) orelse line.len - 1;                try out.appendSlice(self.arena, line[index .. end + 1]);                index = end + 1;                continue;            }            if (line[index] == '\'') {                const end = findQuotedEnd(line, index, '\'') orelse line.len - 1;                try out.appendSlice(self.arena, line[index .. end + 1]);                index = end + 1;                continue;            }            if (!isIdentStart(line[index])) {                try out.append(self.arena, line[index]);                index += 1;                continue;            }            const start = index;            index += 1;            while (index < line.len and isIdentContinue(line[index])) : (index += 1) {}            const name = line[start..index];            if (!std.mem.eql(u8, name, "__has_embed")) {                try out.appendSlice(self.arena, name);                continue;            }            const call = skipHorizontal(line, index);            if (call >= line.len or line[call] != '(') {                try out.appendSlice(self.arena, name);                continue;            }            const end = findBalancedEnd(line, call) orelse {                try out.appendSlice(self.arena, line[start..]);                return out.toOwnedSlice(self.arena);            };            try appendPrint(self.arena, &out, "{d}", .{@backingInt(try self.hasEmbed(line[call + 1 .. end], source_dir))});            replaced.* = true;            index = end + 1;        }        return out.toOwnedSlice(self.arena);    }    fn hasEmbed(self: *Expander, raw: []const u8, source_dir: []const u8) Error!Status {        const expanded = try self.expandMacros(raw);        const request = parseRequest(expanded) orelse return .not_found;        const path = self.findResource(request, source_dir) orelse return .not_found;        const resource = sys.fs.readFileAlloc(self.arena, path, max_resource_bytes) catch return .not_found;        const count = if (request.params.limit) |limit| @min(limit, resource.len) else resource.len;        return if (count == 0) .empty else .found;    }    pub fn expandOutput(self: *Expander, preprocessed: []const u8) Error![]const u8 {        if (self.embeds.items.len == 0) return preprocessed;        var out = std.ArrayListUnmanaged(u8).empty;        var index: usize = 0;        while (index < preprocessed.len) {            if (std.mem.startsWith(u8, preprocessed[index..], sentinel_prefix)) {                var cursor = index + sentinel_prefix.len;                const digits_start = cursor;                while (cursor < preprocessed.len and std.ascii.isDigit(preprocessed[cursor])) : (cursor += 1) {}                if (cursor > digits_start and cursor + 2 <= preprocessed.len and std.mem.eql(u8, preprocessed[cursor .. cursor + 2], "__")) {                    const id = std.fmt.parseInt(usize, preprocessed[digits_start..cursor], 10) catch null;                    if (id) |embed_id| {                        if (embed_id < self.embeds.items.len) {                            const expanded = try self.expandEmbed(self.embeds.items[embed_id]);                            try out.appendSlice(self.arena, expanded);                            index = cursor + 2;                            continue;                        }                    }                }            }            try out.append(self.arena, preprocessed[index]);            index += 1;        }        return out.toOwnedSlice(self.arena);    }    fn expandEmbed(self: *Expander, embed: Embed) Error![]const u8 {        const request = parseRequest(embed.raw) orelse return error.PreprocessFailed;        const path = self.findResource(request, embed.source_dir) orelse return error.PreprocessFailed;        const resource = sys.fs.readFileAlloc(self.arena, path, max_resource_bytes) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            else => return error.PreprocessFailed,        };        const count = if (request.params.limit) |limit| @min(limit, resource.len) else resource.len;        if (count == 0) return request.params.if_empty orelse "";        var out = std.ArrayListUnmanaged(u8).empty;        if (request.params.prefix) |prefix| try out.appendSlice(self.arena, prefix);        for (resource[0..count], 0..) |byte, byte_index| {            if (byte_index != 0) try out.append(self.arena, ',');            try appendPrint(self.arena, &out, "{d}", .{byte});        }        if (request.params.suffix) |suffix| try out.appendSlice(self.arena, suffix);        return out.toOwnedSlice(self.arena);    }    fn findResource(self: *Expander, request: Request, source_dir: []const u8) ?[]const u8 {        if (std.fs.path.isAbsolute(request.name) and fileExists(request.name)) return request.name;        if (request.quoted) {            if (self.findResourceInDir(source_dir, request.name)) |path| return path;            if (self.findResourceInDir(".", request.name)) |path| return path;        }        for (self.options.include_dirs) |dir| {            if (self.findResourceInDir(dir, request.name)) |path| return path;        }        if (!request.quoted) {            if (self.findResourceInDir(source_dir, request.name)) |path| return path;            if (self.findResourceInDir(".", request.name)) |path| return path;        }        return null;    }    fn findInclude(self: *Expander, include: Include, source_dir: []const u8) ?[]const u8 {        if (std.fs.path.isAbsolute(include.name) and fileExists(include.name)) return include.name;        if (include.quoted) {            if (self.findResourceInDir(source_dir, include.name)) |path| return path;            if (self.findResourceInDir(".", include.name)) |path| return path;        }        for (self.options.include_dirs) |dir| {            if (self.findResourceInDir(dir, include.name)) |path| return path;        }        if (!include.quoted) {            if (self.findResourceInDir(source_dir, include.name)) |path| return path;            if (self.findResourceInDir(".", include.name)) |path| return path;        }        return null;    }    fn findResourceInDir(self: *Expander, dir: []const u8, name: []const u8) ?[]const u8 {        const path = std.fs.path.join(self.arena, &.{ dir, name }) catch return null;        if (fileExists(path)) return path;        return null;    }    fn recordDefine(self: *Expander, raw: []const u8) Error!void {        const macro = (try parseDefine(self.arena, raw)) orelse return;        try self.macros.append(self.arena, macro);    }    fn recordUndef(self: *Expander, raw: []const u8) void {        const name = parseUndef(raw) orelse return;        var index: usize = 0;        while (index < self.macros.items.len) {            if (std.mem.eql(u8, self.macros.items[index].name, name)) {                _ = self.macros.orderedRemove(index);                continue;            }            index += 1;        }    }    fn expandMacros(self: *Expander, raw: []const u8) Error![]const u8 {        var current = raw;        var pass: usize = 0;        while (pass < 16) : (pass += 1) {            var changed = false;            const expanded = try self.expandMacrosOnce(current, &changed);            if (!changed) return current;            current = expanded;        }        return current;    }    fn expandMacrosOnce(self: *Expander, raw: []const u8, changed: *bool) Error![]const u8 {        var out = std.ArrayListUnmanaged(u8).empty;        var index: usize = 0;        while (index < raw.len) {            if (raw[index] == '"') {                const end = findStringEnd(raw, index) orelse raw.len - 1;                try out.appendSlice(self.arena, raw[index .. end + 1]);                index = end + 1;                continue;            }            if (raw[index] == '\'') {                const end = findQuotedEnd(raw, index, '\'') orelse raw.len - 1;                try out.appendSlice(self.arena, raw[index .. end + 1]);                index = end + 1;                continue;            }            if (isIdentStart(raw[index])) {                const start = index;                index += 1;                while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}                const name = raw[start..index];                if (self.lookupMacro(name)) |macro| {                    if (!macro.function_like) {                        try out.appendSlice(self.arena, macro.replacement);                        changed.* = true;                    } else {                        const call = skipHorizontal(raw, index);                        if (call >= raw.len or raw[call] != '(') {                            try out.appendSlice(self.arena, name);                        } else if (findBalancedEnd(raw, call)) |end| {                            if (try self.expandFunctionMacro(macro, raw[call + 1 .. end])) |expanded| {                                try out.appendSlice(self.arena, expanded);                                changed.* = true;                                index = end + 1;                            } else {                                try out.appendSlice(self.arena, raw[start .. end + 1]);                                index = end + 1;                            }                        } else {                            try out.appendSlice(self.arena, name);                        }                    }                } else {                    try out.appendSlice(self.arena, name);                }                continue;            }            try out.append(self.arena, raw[index]);            index += 1;        }        return out.toOwnedSlice(self.arena);    }    fn expandFunctionMacro(self: *Expander, macro: Macro, raw_arguments: []const u8) Error!?[]const u8 {        const arguments = try self.parseMacroArguments(macro, raw_arguments) orelse return null;        var expanded = try self.arena.alloc([]const u8, arguments.len);        for (arguments, 0..) |argument, argument_index| {            expanded[argument_index] = try self.expandMacros(argument);        }        return try self.substituteFunctionMacro(macro, expanded);    }    fn parseMacroArguments(self: *Expander, macro: Macro, raw: []const u8) Error!?[]const []const u8 {        var pieces = std.ArrayListUnmanaged([]const u8).empty;        const trimmed = std.mem.trim(u8, raw, " \t\r");        if (trimmed.len == 0) {            if (macro.params.len == 0) return &.{};            try pieces.append(self.arena, "");        } else {            var start: usize = 0;            var index: usize = 0;            var depth: usize = 0;            while (index < raw.len) : (index += 1) {                switch (raw[index]) {                    '"' => index = findStringEnd(raw, index) orelse return null,                    '\'' => index = findQuotedEnd(raw, index, '\'') orelse return null,                    '(' => depth += 1,                    ')' => {                        if (depth > 0) depth -= 1;                    },                    ',' => if (depth == 0) {                        try pieces.append(self.arena, std.mem.trim(u8, raw[start..index], " \t\r"));                        start = index + 1;                    },                    else => {},                }            }            try pieces.append(self.arena, std.mem.trim(u8, raw[start..], " \t\r"));        }        if (!macro.variadic) {            if (pieces.items.len != macro.params.len) return null;            return try pieces.toOwnedSlice(self.arena);        }        const fixed_count = macro.params.len - 1;        if (pieces.items.len < fixed_count) return null;        var arguments = try self.arena.alloc([]const u8, macro.params.len);        for (arguments[0..fixed_count], 0..) |*argument, argument_index| argument.* = pieces.items[argument_index];        if (pieces.items.len == fixed_count) {            arguments[fixed_count] = "";        } else if (pieces.items.len == fixed_count + 1) {            arguments[fixed_count] = pieces.items[fixed_count];        } else {            var joined = std.ArrayListUnmanaged(u8).empty;            for (pieces.items[fixed_count..], 0..) |piece, piece_index| {                if (piece_index != 0) try joined.append(self.arena, ',');                try joined.appendSlice(self.arena, piece);            }            arguments[fixed_count] = try joined.toOwnedSlice(self.arena);        }        return arguments;    }    fn substituteFunctionMacro(self: *Expander, macro: Macro, arguments: []const []const u8) Error![]const u8 {        var out = std.ArrayListUnmanaged(u8).empty;        var index: usize = 0;        while (index < macro.replacement.len) {            if (macro.replacement[index] == '"') {                const end = findStringEnd(macro.replacement, index) orelse macro.replacement.len - 1;                try out.appendSlice(self.arena, macro.replacement[index .. end + 1]);                index = end + 1;                continue;            }            if (macro.replacement[index] == '\'') {                const end = findQuotedEnd(macro.replacement, index, '\'') orelse macro.replacement.len - 1;                try out.appendSlice(self.arena, macro.replacement[index .. end + 1]);                index = end + 1;                continue;            }            if (!isIdentStart(macro.replacement[index])) {                try out.append(self.arena, macro.replacement[index]);                index += 1;                continue;            }            const start = index;            index += 1;            while (index < macro.replacement.len and isIdentContinue(macro.replacement[index])) : (index += 1) {}            const name = macro.replacement[start..index];            if (macro.variadic and std.mem.eql(u8, name, "__VA_OPT__")) {                const call = skipHorizontal(macro.replacement, index);                if (call < macro.replacement.len and macro.replacement[call] == '(') {                    if (findBalancedEnd(macro.replacement, call)) |end| {                        if (std.mem.trim(u8, arguments[arguments.len - 1], " \t\r").len != 0) {                            try out.appendSlice(self.arena, macro.replacement[call + 1 .. end]);                        }                        index = end + 1;                        continue;                    }                }            }            if (paramIndex(macro, name)) |argument_index| {                try out.appendSlice(self.arena, arguments[argument_index]);            } else {                try out.appendSlice(self.arena, name);            }        }        return out.toOwnedSlice(self.arena);    }    fn lookupMacro(self: *const Expander, name: []const u8) ?Macro {        var index = self.macros.items.len;        while (index > 0) {            index -= 1;            const macro = self.macros.items[index];            if (std.mem.eql(u8, macro.name, name)) return macro;        }        if (builtinMacro(name)) |replacement| return .{ .name = name, .replacement = replacement };        return null;    }    fn isMacroDefined(self: *const Expander, name: []const u8) bool {        if (std.mem.eql(u8, name, "__has_embed")) return true;        return self.lookupMacro(name) != null;    }    fn evalCondition(self: *Expander, raw: []const u8, source_dir: []const u8) Error!bool {        var embed_replaced = false;        const with_embed = try self.expandHasEmbedExpressions(raw, source_dir, &embed_replaced);        const with_defined = try self.replaceDefinedOperators(with_embed);        const expanded = try self.expandMacros(with_defined);        var parser = ConditionParser{ .text = expanded };        const value = parser.parse() orelse return true;        return value != 0;    }    fn replaceDefinedOperators(self: *Expander, raw: []const u8) Error![]const u8 {        var out = std.ArrayListUnmanaged(u8).empty;        var index: usize = 0;        while (index < raw.len) {            if (raw[index] == '"') {                const end = findStringEnd(raw, index) orelse raw.len - 1;                try out.appendSlice(self.arena, raw[index .. end + 1]);                index = end + 1;                continue;            }            if (raw[index] == '\'') {                const end = findQuotedEnd(raw, index, '\'') orelse raw.len - 1;                try out.appendSlice(self.arena, raw[index .. end + 1]);                index = end + 1;                continue;            }            if (!isIdentStart(raw[index])) {                try out.append(self.arena, raw[index]);                index += 1;                continue;            }            const start = index;            index += 1;            while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}            const name = raw[start..index];            if (!std.mem.eql(u8, name, "defined")) {                try out.appendSlice(self.arena, name);                continue;            }            var cursor = skipHorizontal(raw, index);            var parenthesized = false;            if (cursor < raw.len and raw[cursor] == '(') {                parenthesized = true;                cursor = skipHorizontal(raw, cursor + 1);            }            if (cursor >= raw.len or !isIdentStart(raw[cursor])) {                try out.appendSlice(self.arena, name);                continue;            }            const macro_start = cursor;            cursor += 1;            while (cursor < raw.len and isIdentContinue(raw[cursor])) : (cursor += 1) {}            const macro_name = raw[macro_start..cursor];            cursor = skipHorizontal(raw, cursor);            if (parenthesized) {                if (cursor >= raw.len or raw[cursor] != ')') {                    try out.appendSlice(self.arena, name);                    continue;                }                cursor += 1;            }            try out.append(self.arena, if (self.isMacroDefined(macro_name)) '1' else '0');            index = cursor;        }        return out.toOwnedSlice(self.arena);    }};const ConditionParser = struct {    text: []const u8,    index: usize = 0,    fn parse(self: *ConditionParser) ?i128 {        const value = self.parseOr() orelse return null;        self.skip();        if (self.index != self.text.len) return null;        return value;    }    fn parseOr(self: *ConditionParser) ?i128 {        var value = self.parseAnd() orelse return null;        while (self.consume("||")) {            const right = self.parseAnd() orelse return null;            value = if (value != 0 or right != 0) 1 else 0;        }        return value;    }    fn parseAnd(self: *ConditionParser) ?i128 {        var value = self.parseBitOr() orelse return null;        while (self.consume("&&")) {            const right = self.parseBitOr() orelse return null;            value = if (value != 0 and right != 0) 1 else 0;        }        return value;    }    fn parseBitOr(self: *ConditionParser) ?i128 {        var value = self.parseBitXor() orelse return null;        while (self.consumeSingle('|', "||")) {            const right = self.parseBitXor() orelse return null;            value |= right;        }        return value;    }    fn parseBitXor(self: *ConditionParser) ?i128 {        var value = self.parseBitAnd() orelse return null;        while (self.consume("^")) {            const right = self.parseBitAnd() orelse return null;            value ^= right;        }        return value;    }    fn parseBitAnd(self: *ConditionParser) ?i128 {        var value = self.parseEquality() orelse return null;        while (self.consumeSingle('&', "&&")) {            const right = self.parseEquality() orelse return null;            value &= right;        }        return value;    }    fn parseEquality(self: *ConditionParser) ?i128 {        var value = self.parseRelational() orelse return null;        while (true) {            if (self.consume("==")) {                const right = self.parseRelational() orelse return null;                value = if (value == right) 1 else 0;            } else if (self.consume("!=")) {                const right = self.parseRelational() orelse return null;                value = if (value != right) 1 else 0;            } else {                return value;            }        }    }    fn parseRelational(self: *ConditionParser) ?i128 {        var value = self.parseShift() orelse return null;        while (true) {            if (self.consume("<=")) {                const right = self.parseShift() orelse return null;                value = if (value <= right) 1 else 0;            } else if (self.consume(">=")) {                const right = self.parseShift() orelse return null;                value = if (value >= right) 1 else 0;            } else if (self.consume("<")) {                const right = self.parseShift() orelse return null;                value = if (value < right) 1 else 0;            } else if (self.consume(">")) {                const right = self.parseShift() orelse return null;                value = if (value > right) 1 else 0;            } else {                return value;            }        }    }    fn parseShift(self: *ConditionParser) ?i128 {        var value = self.parseAdd() orelse return null;        while (true) {            if (self.consume("<<")) {                const right = self.parseAdd() orelse return null;                if (right < 0 or right > 127) return null;                value = value << @intCast(right);            } else if (self.consume(">>")) {                const right = self.parseAdd() orelse return null;                if (right < 0 or right > 127) return null;                value = value >> @intCast(right);            } else {                return value;            }        }    }    fn parseAdd(self: *ConditionParser) ?i128 {        var value = self.parseMul() orelse return null;        while (true) {            if (self.consume("+")) {                const right = self.parseMul() orelse return null;                value += right;            } else if (self.consume("-")) {                const right = self.parseMul() orelse return null;                value -= right;            } else {                return value;            }        }    }    fn parseMul(self: *ConditionParser) ?i128 {        var value = self.parseUnary() orelse return null;        while (true) {            if (self.consume("*")) {                const right = self.parseUnary() orelse return null;                value *= right;            } else if (self.consume("/")) {                const right = self.parseUnary() orelse return null;                if (right == 0) return null;                value = @divTrunc(value, right);            } else if (self.consume("%")) {                const right = self.parseUnary() orelse return null;                if (right == 0) return null;                value = @rem(value, right);            } else {                return value;            }        }    }    fn parseUnary(self: *ConditionParser) ?i128 {        if (self.consume("!")) return if ((self.parseUnary() orelse return null) == 0) 1 else 0;        if (self.consume("+")) return self.parseUnary();        if (self.consume("-")) return -(self.parseUnary() orelse return null);        if (self.consume("~")) return ~(self.parseUnary() orelse return null);        return self.parsePrimary();    }    fn parsePrimary(self: *ConditionParser) ?i128 {        self.skip();        if (self.index >= self.text.len) return null;        if (self.text[self.index] == '(') {            self.index += 1;            const value = self.parseOr() orelse return null;            self.skip();            if (self.index >= self.text.len or self.text[self.index] != ')') return null;            self.index += 1;            return value;        }        if (self.text[self.index] == '\'') return self.parseChar();        if (std.ascii.isDigit(self.text[self.index])) return self.parseNumber();        if (isIdentStart(self.text[self.index])) {            self.index += 1;            while (self.index < self.text.len and isIdentContinue(self.text[self.index])) : (self.index += 1) {}            self.skip();            if (self.index < self.text.len and self.text[self.index] == '(') return null;            return 0;        }        return null;    }    fn parseNumber(self: *ConditionParser) ?i128 {        const start = self.index;        var base: u8 = 10;        if (self.index + 2 <= self.text.len and self.text[self.index] == '0' and (self.text[self.index + 1] == 'x' or self.text[self.index + 1] == 'X')) {            base = 16;            self.index += 2;        }        const digits_start = self.index;        while (self.index < self.text.len) : (self.index += 1) {            if (base == 16) {                if (!std.ascii.isHex(self.text[self.index])) break;            } else if (!std.ascii.isDigit(self.text[self.index])) {                break;            }        }        if (self.index == digits_start) return null;        const digits = self.text[digits_start..self.index];        const value = std.fmt.parseInt(i128, digits, base) catch return null;        while (self.index < self.text.len and (isIdentContinue(self.text[self.index]) or self.text[self.index] == '\'')) : (self.index += 1) {}        _ = start;        return value;    }    fn parseChar(self: *ConditionParser) ?i128 {        self.index += 1;        if (self.index >= self.text.len) return null;        const value: i128 = if (self.text[self.index] == '\\') value: {            self.index += 1;            if (self.index >= self.text.len) return null;            break :value switch (self.text[self.index]) {                'n' => 10,                'r' => 13,                't' => 9,                '0' => 0,                else => self.text[self.index],            };        } else self.text[self.index];        self.index += 1;        if (self.index >= self.text.len or self.text[self.index] != '\'') return null;        self.index += 1;        return value;    }    fn consume(self: *ConditionParser, token: []const u8) bool {        self.skip();        if (!std.mem.startsWith(u8, self.text[self.index..], token)) return false;        self.index += token.len;        return true;    }    fn consumeSingle(self: *ConditionParser, token: u8, excluded: []const u8) bool {        self.skip();        if (std.mem.startsWith(u8, self.text[self.index..], excluded)) return false;        if (self.index >= self.text.len or self.text[self.index] != token) return false;        self.index += 1;        return true;    }    fn skip(self: *ConditionParser) void {        self.index = skipHorizontal(self.text, self.index);    }};fn rewriteHasEmbedDefined(expander: *Expander, out: *std.ArrayListUnmanaged(u8), direct: Directive) Error!bool {    const positive = std.mem.eql(u8, direct.name, "ifdef") or std.mem.eql(u8, direct.name, "elifdef");    const negative = std.mem.eql(u8, direct.name, "ifndef") or std.mem.eql(u8, direct.name, "elifndef");    if (!positive and !negative) return false;    if (!std.mem.eql(u8, std.mem.trim(u8, direct.argument, " \t\r"), "__has_embed")) return false;    const elif = std.mem.startsWith(u8, direct.name, "elif");    try out.appendSlice(expander.arena, if (elif) "#elif " else "#if ");    try out.append(expander.arena, if (positive) '1' else '0');    expander.changed = true;    return true;}fn parseRequest(raw: []const u8) ?Request {    var index = skipHorizontal(raw, 0);    if (index >= raw.len) return null;    const quoted = switch (raw[index]) {        '"' => true,        '<' => false,        else => return null,    };    const end = if (quoted)        findStringEnd(raw, index) orelse return null    else        std.mem.indexOfScalarPos(u8, raw, index + 1, '>') orelse return null;    const name = raw[index + 1 .. end];    index = skipHorizontal(raw, end + 1);    const params = parseParams(raw[index..]) orelse return null;    return .{ .name = name, .quoted = quoted, .params = params };}fn parseInclude(raw: []const u8) ?Include {    var index = skipHorizontal(raw, 0);    if (index >= raw.len) return null;    const quoted = switch (raw[index]) {        '"' => true,        '<' => false,        else => return null,    };    const end = if (quoted)        findStringEnd(raw, index) orelse return null    else        std.mem.indexOfScalarPos(u8, raw, index + 1, '>') orelse return null;    const name = raw[index + 1 .. end];    index = skipHorizontal(raw, end + 1);    if (index != raw.len) return null;    return .{ .name = name, .quoted = quoted };}fn parseDefine(arena: std.mem.Allocator, raw: []const u8) Error!?Macro {    var index = skipHorizontal(raw, 0);    if (index >= raw.len or !isIdentStart(raw[index])) return null;    const start = index;    index += 1;    while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}    const name = raw[start..index];    if (index < raw.len and raw[index] == '(') {        index += 1;        var params = std.ArrayListUnmanaged([]const u8).empty;        var variadic = false;        index = skipHorizontal(raw, index);        if (index < raw.len and raw[index] == ')') {            index += 1;        } else {            while (index < raw.len) {                index = skipHorizontal(raw, index);                if (index >= raw.len) return null;                if (index + 3 <= raw.len and std.mem.eql(u8, raw[index .. index + 3], "...")) {                    try params.append(arena, "__VA_ARGS__");                    variadic = true;                    index += 3;                    index = skipHorizontal(raw, index);                    if (index >= raw.len or raw[index] != ')') return null;                    index += 1;                    break;                }                if (!isIdentStart(raw[index])) return null;                const param_start = index;                index += 1;                while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}                const param = raw[param_start..index];                for (params.items) |existing| {                    if (std.mem.eql(u8, existing, param)) return null;                }                try params.append(arena, param);                index = skipHorizontal(raw, index);                if (index >= raw.len) return null;                if (raw[index] == ',') {                    index += 1;                    continue;                }                if (raw[index] == ')') {                    index += 1;                    break;                }                return null;            }        }        const replacement = std.mem.trim(u8, raw[index..], " \t\r");        return .{            .name = name,            .replacement = replacement,            .params = try params.toOwnedSlice(arena),            .function_like = true,            .variadic = variadic,        };    }    const replacement = std.mem.trim(u8, raw[index..], " \t\r");    return .{ .name = name, .replacement = replacement };}fn paramIndex(macro: Macro, name: []const u8) ?usize {    for (macro.params, 0..) |param, index| {        if (std.mem.eql(u8, param, name)) return index;    }    return null;}fn builtinMacro(name: []const u8) ?[]const u8 {    if (std.mem.eql(u8, name, "__STDC_EMBED_NOT_FOUND__")) return "0";    if (std.mem.eql(u8, name, "__STDC_EMBED_FOUND__")) return "1";    if (std.mem.eql(u8, name, "__STDC_EMBED_EMPTY__")) return "2";    return null;}fn parseUndef(raw: []const u8) ?[]const u8 {    var index = skipHorizontal(raw, 0);    if (index >= raw.len or !isIdentStart(raw[index])) return null;    const start = index;    index += 1;    while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}    const name = raw[start..index];    index = skipHorizontal(raw, index);    if (index != raw.len) return null;    return name;}fn parseParams(raw: []const u8) ?Params {    var params: Params = .{};    var index: usize = 0;    while (true) {        index = skipHorizontal(raw, index);        if (index >= raw.len) return params;        const name_start = index;        if (!isIdentStart(raw[index])) return null;        index += 1;        while (index < raw.len and isIdentContinue(raw[index])) : (index += 1) {}        const name = raw[name_start..index];        index = skipHorizontal(raw, index);        if (index >= raw.len or raw[index] != '(') return null;        const end = findBalancedEnd(raw, index) orelse return null;        const body = std.mem.trim(u8, raw[index + 1 .. end], " \t\r");        if (std.mem.eql(u8, name, "limit")) {            if (params.limit != null) return null;            params.limit = parseLimit(body) orelse return null;        } else if (std.mem.eql(u8, name, "prefix")) {            if (params.prefix != null) return null;            params.prefix = body;        } else if (std.mem.eql(u8, name, "suffix")) {            if (params.suffix != null) return null;            params.suffix = body;        } else if (std.mem.eql(u8, name, "if_empty")) {            if (params.if_empty != null) return null;            params.if_empty = body;        } else {            return null;        }        index = end + 1;    }}fn parseLimit(raw: []const u8) ?usize {    if (raw.len == 0) return null;    if (std.mem.startsWith(u8, raw, "0x") or std.mem.startsWith(u8, raw, "0X")) return std.fmt.parseInt(usize, raw[2..], 16) catch null;    return std.fmt.parseInt(usize, raw, 10) catch null;}fn parseDirective(line: []const u8) ?Directive {    var index = skipHorizontal(line, 0);    if (index >= line.len or line[index] != '#') return null;    index = skipHorizontal(line, index + 1);    if (index >= line.len or !isIdentStart(line[index])) return null;    const start = index;    index += 1;    while (index < line.len and isIdentContinue(line[index])) : (index += 1) {}    return .{ .name = line[start..index], .argument = line[index..] };}fn findStringEnd(raw: []const u8, start: usize) ?usize {    return findQuotedEnd(raw, start, '"');}fn findQuotedEnd(raw: []const u8, start: usize, quote: u8) ?usize {    var index = start + 1;    while (index < raw.len) : (index += 1) {        if (raw[index] == '\\') {            index += 1;            continue;        }        if (raw[index] == quote) return index;    }    return null;}fn findBalancedEnd(raw: []const u8, start: usize) ?usize {    var depth: usize = 0;    var index = start;    while (index < raw.len) : (index += 1) {        switch (raw[index]) {            '"' => index = findStringEnd(raw, index) orelse return null,            '(' => depth += 1,            ')' => {                depth -= 1;                if (depth == 0) return index;            },            else => {},        }    }    return null;}fn appendLineMarker(arena: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), path: []const u8) Error!void {    try out.appendSlice(arena, "#line 1 \"");    for (path) |byte| {        if (byte == '"' or byte == '\\') try out.append(arena, '\\');        try out.append(arena, byte);    }    try out.appendSlice(arena, "\"\n");}fn appendPrint(    arena: std.mem.Allocator,    out: *std.ArrayListUnmanaged(u8),    comptime fmt: []const u8,    args: anytype,) Error!void {    const text = std.fmt.allocPrint(arena, fmt, args) catch return error.OutOfMemory;    try out.appendSlice(arena, text);}fn sourceDirectory(arena: std.mem.Allocator, source_path: []const u8) ![]const u8 {    const dir = std.fs.path.dirname(source_path) orelse ".";    return std.fs.path.resolve(arena, &.{dir});}fn fileExists(path: []const u8) bool {    sys.fs.cwd().access(sys.fs.debugIo(), path, .{}) catch return false;    return true;}fn skipHorizontal(text: []const u8, start: usize) usize {    var index = start;    while (index < text.len and isHorizontal(text[index])) : (index += 1) {}    return index;}fn isHorizontal(byte: u8) bool {    return byte == ' ' or byte == '\t' or byte == '\r';}fn isIdentStart(byte: u8) bool {    return std.ascii.isAlphabetic(byte) or byte == '_';}fn isIdentContinue(byte: u8) bool {    return isIdentStart(byte) or std.ascii.isDigit(byte);}

Source: lib/chant/src/preprocess/root.zig:4

zig
pub const embed = @import("embed.zig");

Audit

Definitions5
Public names5
Members8
Version26.7.0
Revisiondaab053ee433