mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
lots
This commit is contained in:
@@ -11,9 +11,16 @@ pub fn build(b: *std.build.Builder) void {
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const exe = b.addExecutable("esdev", "src/main.zig");
|
||||
var exe: *std.build.LibExeObjStep = undefined;
|
||||
if (target.getCpuArch().isWasm()) {
|
||||
exe = b.addExecutable("esdev", "src/main_wasm.zig");
|
||||
} else {
|
||||
exe = b.addExecutable("esdev", "src/main.zig");
|
||||
}
|
||||
|
||||
exe.setTarget(target);
|
||||
exe.setBuildMode(mode);
|
||||
|
||||
exe.addLibPath("/usr/local/lib");
|
||||
exe.install();
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
const std = @import("std");
|
||||
const options = @import("options.zig");
|
||||
const logger = @import("logger.zig");
|
||||
const js_ast = @import("js_ast.zig");
|
||||
|
||||
pub const Bundler = struct {
|
||||
options: options.TransformOptions,
|
||||
options: options.TransformOptions,
|
||||
logger: logger.Log,
|
||||
|
||||
pub fn init(options: options.TransformOptions, allocator: *std.mem.Allocator) Bundler {
|
||||
var log = logger.Log{ .msgs = ArrayList(Msg).init(allocator) };
|
||||
}
|
||||
|
||||
pub fn
|
||||
|
||||
}
|
||||
pub fn scan() void {}
|
||||
};
|
||||
|
||||
3
src/flags.zig
Normal file
3
src/flags.zig
Normal file
@@ -0,0 +1,3 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const isWasm = std.Target.Os.Tag.freestanding == std.Target.current.os.tag;
|
||||
60
src/fs.zig
60
src/fs.zig
@@ -1,8 +1,12 @@
|
||||
const std = @import("std");
|
||||
const strings = @import("strings.zig");
|
||||
|
||||
usingnamespace @import("strings.zig");
|
||||
|
||||
const alloc = @import("alloc.zig");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
// pub const FilesystemImplementation = @import("fs_impl.zig");
|
||||
|
||||
pub const FileSystem = struct { tree: std.AutoHashMap(FileSystemEntry) };
|
||||
|
||||
pub const FileSystemEntry = union(enum) {
|
||||
@@ -10,22 +14,44 @@ pub const FileSystemEntry = union(enum) {
|
||||
directory: Directory,
|
||||
};
|
||||
|
||||
pub const File = struct { path: Path, mtime: ?usize, contents: ?[]u8 };
|
||||
pub const File = struct {
|
||||
path: Path,
|
||||
mtime: ?usize,
|
||||
contents: ?string,
|
||||
};
|
||||
pub const Directory = struct { path: Path, mtime: ?usize, contents: []FileSystemEntry };
|
||||
|
||||
pub const PathName = struct {
|
||||
base: []u8,
|
||||
dir: []u8,
|
||||
ext: []u8,
|
||||
base: string,
|
||||
dir: string,
|
||||
ext: string,
|
||||
|
||||
pub fn init(_path: []const u8, allocator: *std.mem.Allocator) PathName {
|
||||
// TODO: leak.
|
||||
var path: []u8 = allocator.alloc(u8, _path.len) catch unreachable;
|
||||
std.mem.copy(u8, path, _path);
|
||||
// For readability, the names of certain automatically-generated symbols are
|
||||
// derived from the file name. For example, instead of the CommonJS wrapper for
|
||||
// a file being called something like "require273" it can be called something
|
||||
// like "require_react" instead. This function generates the part of these
|
||||
// identifiers that's specific to the file path. It can take both an absolute
|
||||
// path (OS-specific) and a path in the source code (OS-independent).
|
||||
//
|
||||
// Note that these generated names do not at all relate to the correctness of
|
||||
// the code as far as avoiding symbol name collisions. These names still go
|
||||
// through the renaming logic that all other symbols go through to avoid name
|
||||
// collisions.
|
||||
pub fn nonUniqueNameString(self: *PathName, allocator: *std.mem.Allocator) !string {
|
||||
if (strings.eql("index", self.base)) {
|
||||
if (self.dir.len > 0) {
|
||||
return MutableString.ensureValidIdentifier(PathName.init(self.dir), allocator);
|
||||
}
|
||||
}
|
||||
|
||||
return MutableString.ensureValidIdentifier(self.base, allocator);
|
||||
}
|
||||
|
||||
pub fn init(_path: string) PathName {
|
||||
var path = _path;
|
||||
var base = path;
|
||||
var dir = path;
|
||||
var ext = path;
|
||||
var dir = path;
|
||||
|
||||
var _i = strings.lastIndexOfChar(path, '/');
|
||||
while (_i) |i| {
|
||||
@@ -58,13 +84,13 @@ pub const PathName = struct {
|
||||
};
|
||||
|
||||
pub const Path = struct {
|
||||
pretty_path: []const u8,
|
||||
text: []const u8,
|
||||
namespace: []const u8,
|
||||
pretty_path: string,
|
||||
text: string,
|
||||
namespace: string,
|
||||
name: PathName,
|
||||
|
||||
pub fn init(text: []const u8, allocator: *std.mem.Allocator) Path {
|
||||
return Path{ .pretty_path = text, .text = text, .namespace = "file", .name = PathName.init(text, allocator) };
|
||||
pub fn init(text: string) Path {
|
||||
return Path{ .pretty_path = text, .text = text, .namespace = "file", .name = PathName.init(text) };
|
||||
}
|
||||
|
||||
pub fn isBefore(a: *Path, b: Path) bool {
|
||||
@@ -77,7 +103,9 @@ pub const Path = struct {
|
||||
|
||||
test "PathName.init" {
|
||||
var file = "/root/directory/file.ext".*;
|
||||
const res = PathName.init(&file, std.heap.page_allocator);
|
||||
const res = PathName.init(
|
||||
&file,
|
||||
);
|
||||
std.testing.expectEqualStrings(res.dir, "/root/directory");
|
||||
std.testing.expectEqualStrings(res.base, "file");
|
||||
std.testing.expectEqualStrings(res.ext, ".ext");
|
||||
|
||||
10
src/fs_impl.zig
Normal file
10
src/fs_impl.zig
Normal file
@@ -0,0 +1,10 @@
|
||||
const std = @import("std");
|
||||
usingnamespace @import("flags.zig");
|
||||
|
||||
pub const FS = comptime {
|
||||
if (isWASM) {
|
||||
return @import("fs_impl_wasm.zig");
|
||||
} else {
|
||||
return @import("fs_impl_native.zig");
|
||||
}
|
||||
};
|
||||
3
src/fs_impl_native.zig
Normal file
3
src/fs_impl_native.zig
Normal file
@@ -0,0 +1,3 @@
|
||||
const std = @import("std");
|
||||
|
||||
const fs = std.fs;
|
||||
0
src/fs_impl_wasm.zig
Normal file
0
src/fs_impl_wasm.zig
Normal file
1189
src/js_ast.zig
1189
src/js_ast.zig
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@ const tables = @import("js_lexer_tables.zig");
|
||||
const alloc = @import("alloc.zig");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
usingnamespace @import("strings.zig");
|
||||
|
||||
const _f = @import("./test/fixtures.zig");
|
||||
|
||||
const unicode = std.unicode;
|
||||
@@ -18,8 +20,6 @@ pub const jsxEntity = tables.jsxEntity;
|
||||
// TODO: JSON
|
||||
const IS_JSON_FILE = false;
|
||||
|
||||
const string = []const u8;
|
||||
|
||||
pub const Lexer = struct {
|
||||
// pub const Error = error{
|
||||
// UnexpectedToken,
|
||||
@@ -1158,7 +1158,10 @@ fn test_lexer(contents: []const u8) Lexer {
|
||||
.msgs = msgs,
|
||||
};
|
||||
|
||||
const source = logger.Source.initPathString("index.js", contents, std.heap.page_allocator);
|
||||
const source = logger.Source.initPathString(
|
||||
"index.js",
|
||||
contents,
|
||||
);
|
||||
return Lexer.init(log, source, alloc.dynamic) catch unreachable;
|
||||
}
|
||||
|
||||
@@ -1208,7 +1211,10 @@ test "Lexer.step()" {
|
||||
};
|
||||
|
||||
defer std.testing.allocator.free(msgs.items);
|
||||
const source = logger.Source.initPathString("index.js", "for (let i = 0; i < 100; i++) { console.log('hi'); }", std.heap.page_allocator);
|
||||
const source = logger.Source.initPathString(
|
||||
"index.js",
|
||||
"for (let i = 0; i < 100; i++) { console.log('hi'); }",
|
||||
);
|
||||
|
||||
var lex = try Lexer.init(log, source, std.testing.allocator);
|
||||
std.testing.expect('f' == lex.code_point);
|
||||
|
||||
@@ -2,9 +2,3 @@ const std = @import("std");
|
||||
const logger = @import("logger.zig");
|
||||
const lexer = @import("lexer.zig");
|
||||
const ast = @import("js_ast.zig");
|
||||
|
||||
pub fn Parse(
|
||||
log: logger.Log,
|
||||
source: logger.Source,
|
||||
|
||||
)
|
||||
@@ -1,5 +1,7 @@
|
||||
const std = @import("std");
|
||||
const strings = @import("strings.zig");
|
||||
|
||||
usingnamespace @import("strings.zig");
|
||||
|
||||
const fs = @import("fs.zig");
|
||||
const unicode = std.unicode;
|
||||
|
||||
@@ -13,7 +15,7 @@ pub const Kind = enum {
|
||||
note,
|
||||
debug,
|
||||
|
||||
pub fn string(self: Kind) []const u8 {
|
||||
pub fn string(self: Kind) string {
|
||||
return switch (self) {
|
||||
.err => "error",
|
||||
.warn => "warn",
|
||||
@@ -26,13 +28,13 @@ pub const Kind = enum {
|
||||
pub const Loc = i32;
|
||||
|
||||
pub const Location = struct {
|
||||
file: []const u8,
|
||||
namespace: []const u8 = "file",
|
||||
file: string,
|
||||
namespace: string = "file",
|
||||
line: i32 = 1, // 1-based
|
||||
column: i32 = 0, // 0-based, in bytes
|
||||
length: usize = 0, // in bytes
|
||||
line_text: ?[]const u8 = null,
|
||||
suggestion: ?[]const u8 = null,
|
||||
line_text: ?string = null,
|
||||
suggestion: ?string = null,
|
||||
|
||||
pub fn init(file: []u8, namespace: []u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
|
||||
return Location{
|
||||
@@ -62,7 +64,7 @@ pub const Location = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_file(file: []const u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
|
||||
pub fn init_file(file: string, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
|
||||
var namespace = "file".*;
|
||||
|
||||
return Location{
|
||||
@@ -84,7 +86,11 @@ pub const Msg = struct {
|
||||
data: Data,
|
||||
};
|
||||
|
||||
pub const Range = struct { loc: Loc = 0, len: i32 = 0 };
|
||||
pub const Range = struct {
|
||||
loc: Loc = 0,
|
||||
len: i32 = 0,
|
||||
const Empty = Range{ .loc = 0, .len = 0 };
|
||||
};
|
||||
|
||||
pub const Log = struct {
|
||||
debug: bool = false,
|
||||
@@ -178,17 +184,22 @@ pub fn usize2Loc(loc: usize) Loc {
|
||||
pub const Source = struct {
|
||||
path: fs.Path,
|
||||
index: u32 = 0,
|
||||
contents: []const u8,
|
||||
contents: string,
|
||||
|
||||
// An identifier that is mixed in to automatically-generated symbol names to
|
||||
// improve readability. For example, if the identifier is "util" then the
|
||||
// symbol for an "export default" statement will be called "util_default".
|
||||
identifier_name: []u8,
|
||||
identifier_name: string,
|
||||
|
||||
pub const ErrorPosition = struct { line_start: usize, line_end: usize, column_count: usize, line_count: usize };
|
||||
|
||||
pub fn initPathString(pathString: []const u8, contents: []const u8, allocator: *std.mem.Allocator) Source {
|
||||
const path = fs.Path.init(pathString, allocator);
|
||||
pub fn initFile(file: fs.File, allocator: *std.mem.Allocator) Source {
|
||||
std.debug.assert(file.contents != null);
|
||||
return Source{ .path = path, .identifier_name = file.path.name.nonUniqueNameString(allocator) catch unreachable, .contents = file.contents };
|
||||
}
|
||||
|
||||
pub fn initPathString(pathString: string, contents: string) Source {
|
||||
const path = fs.Path.init(pathString);
|
||||
return Source{ .path = path, .identifier_name = path.name.base, .contents = contents };
|
||||
}
|
||||
|
||||
@@ -256,7 +267,7 @@ pub const Source = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn rangeData(source: ?Source, r: Range, text: []u8) Data {
|
||||
fn rangeData(source: ?Source, r: Range, text: []u8) Data {
|
||||
return Data{ .text = text, .location = Location.init_or_nil(source, r) };
|
||||
}
|
||||
|
||||
|
||||
13
src/main.zig
13
src/main.zig
@@ -1,7 +1,18 @@
|
||||
const std = @import("std");
|
||||
const lex = @import("js_lexer.zig");
|
||||
const alloc = @import("alloc.zig");
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
std.log.info("All your codebase are belong to us. {s}", .{lex.Keywords.get("hey")});
|
||||
const args = try std.process.argsAlloc(alloc.dynamic);
|
||||
const stdout = std.io.getStdOut();
|
||||
const stderr = std.io.getStdErr();
|
||||
|
||||
if (args.len < 1) {
|
||||
const len = stderr.write("Pass a file path");
|
||||
return;
|
||||
}
|
||||
|
||||
// const path = try std.fs.realpathAlloc(alloc.dynamic, args[args.len - 1]);
|
||||
// const file = try std.fs.openFileAbsolute(path, std.fs.File.OpenFlags{});
|
||||
// const bytes = try file.readToEndAlloc(alloc.dynamic, std.math.maxInt(usize));
|
||||
}
|
||||
|
||||
31
src/main_wasm.zig
Normal file
31
src/main_wasm.zig
Normal file
@@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
const lex = @import("js_lexer.zig");
|
||||
const logger = @import("logger.zig");
|
||||
const alloc = @import("alloc.zig");
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
try alloc.setup(std.heap.page_allocator);
|
||||
// const args = try std.process.argsAlloc(alloc.dynamic);
|
||||
// // const stdout = std.io.getStdOut();
|
||||
// // const stderr = std.io.getStdErr();
|
||||
|
||||
// // if (args.len < 1) {
|
||||
// // const len = stderr.write("Pass a file");
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // alloc
|
||||
const msgs = std.ArrayList(logger.Msg).init(alloc.dynamic);
|
||||
const log = logger.Log{
|
||||
.msgs = msgs,
|
||||
};
|
||||
|
||||
const source = logger.Source.initPathString("index.js", "for (let i = 0; i < 100; i++) { console.log('hi') aposkdpoaskdpokasdpokasdpokasdpokasdpoaksdpoaksdpoaskdpoaksdpoaksdpoaskdpoaskdpoasdk; }", alloc.dynamic);
|
||||
|
||||
var lexer = try lex.Lexer.init(log, source, alloc.dynamic);
|
||||
lexer.next();
|
||||
while (lexer.token != lex.T.t_end_of_file) {
|
||||
lexer.next();
|
||||
}
|
||||
const v = try std.io.getStdOut().write("Finished");
|
||||
}
|
||||
@@ -1,26 +1,74 @@
|
||||
const std = @import("std");
|
||||
const log = @import("logger.zig");
|
||||
const fs = @import("fs.zig");
|
||||
|
||||
usingnamespace @import("strings.zig");
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const Loader = enum {
|
||||
jsx,
|
||||
js,
|
||||
ts,
|
||||
tsx,
|
||||
css,
|
||||
file,
|
||||
};
|
||||
|
||||
pub const defaultLoaders = std.ComptimeStringMap(Loader, .{
|
||||
.{ ".jsx", Loader.jsx },
|
||||
.{ ".js", Loader.js },
|
||||
.{ ".mjs", Loader.js },
|
||||
.{ ".css", Loader.css },
|
||||
.{ ".ts", Loader.ts },
|
||||
.{ ".tsx", Loader.tsx },
|
||||
});
|
||||
|
||||
pub const TransformOptions = struct {
|
||||
footer: []u8 = "",
|
||||
banner: []u8 = "",
|
||||
define: std.StringHashMap([]u8),
|
||||
footer: []const u8 = "",
|
||||
banner: []const u8 = "",
|
||||
define: std.StringHashMap(string),
|
||||
loader: Loader = Loader.tsx,
|
||||
resolve_dir: []u8 = "/",
|
||||
resolve_dir: []const u8 = "/",
|
||||
react_fast_refresh: bool = false,
|
||||
jsx_factory: []u8 = "React.createElement",
|
||||
jsx_pragma: []u8 = "jsx",
|
||||
inject: [][]u8,
|
||||
public_url: []u8,
|
||||
jsx_factory: []const u8 = "React.createElement",
|
||||
jsx_pragma: []const u8 = "jsx",
|
||||
inject: ?[][]const u8 = null,
|
||||
public_url: []const u8 = "/",
|
||||
filesystem_cache: std.StringHashMap(fs.File),
|
||||
entry_point: fs.File,
|
||||
entry_point: *fs.File,
|
||||
|
||||
pub fn initUncached(allocator: *std.mem.Allocator, entryPointName: string, code: string) !TransformOptions {
|
||||
assert(entryPointName.len > 0);
|
||||
|
||||
const filesystemCache = std.StringHashMap(string).init(allocator);
|
||||
|
||||
var entryPoint = !allocator.Create(fs.file);
|
||||
entryPoint.path = fs.Path.init(entryPointName, allocator);
|
||||
entryPoint.contents = code;
|
||||
|
||||
const define = std.StringHashMap(string).init(allocator);
|
||||
try define.ensureCapacity(1);
|
||||
|
||||
define.putAssumeCapacity("process.env.NODE_ENV", "development");
|
||||
|
||||
var loader = Loader.file;
|
||||
if (defaultLoaders.get(entryPoint.path.name.ext)) |defaultLoader| {
|
||||
loader = defaultLoader;
|
||||
}
|
||||
|
||||
assert(loader != .file);
|
||||
assert(code.len > 0);
|
||||
try filesystemCache.put(entryPointName, entryPoint);
|
||||
|
||||
return TransformOptions{
|
||||
.entry_point = entryPoint,
|
||||
.define = define,
|
||||
.loader = loader,
|
||||
.filesystem_cache = filesystemCache,
|
||||
.resolve_dir = entryPoint.path.name.dir,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const OutputFile = struct {
|
||||
|
||||
28
src/string_immutable.zig
Normal file
28
src/string_immutable.zig
Normal file
@@ -0,0 +1,28 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
usingnamespace @import("string_types.zig");
|
||||
|
||||
pub fn containsChar(self: string, char: u8) bool {
|
||||
return std.mem(char) != null;
|
||||
}
|
||||
|
||||
pub fn indexOfChar(self: string, char: u8) ?usize {
|
||||
return std.mem.indexOfScalar(@TypeOf(char), self, char);
|
||||
}
|
||||
|
||||
pub fn lastIndexOfChar(self: string, char: u8) ?usize {
|
||||
return std.mem.lastIndexOfScalar(u8, self, char);
|
||||
}
|
||||
|
||||
pub fn lastIndexOf(self: string, str: u8) ?usize {
|
||||
return std.mem.lastIndexOf(u8, self, str);
|
||||
}
|
||||
|
||||
pub fn indexOf(self: string, str: u8) ?usize {
|
||||
return std.mem.indexOf(u8, self, str);
|
||||
}
|
||||
|
||||
pub fn eql(self: string, other: anytype) bool {
|
||||
return std.mem.eql(u8, self, other);
|
||||
}
|
||||
126
src/string_mutable.zig
Normal file
126
src/string_mutable.zig
Normal file
@@ -0,0 +1,126 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
usingnamespace @import("string_types.zig");
|
||||
|
||||
pub const MutableString = struct {
|
||||
allocator: *std.mem.Allocator,
|
||||
list: std.ArrayListUnmanaged(u8),
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator, capacity: usize) !MutableString {
|
||||
return MutableString{ .allocator = allocator, .list = try std.ArrayListUnmanaged(u8).initCapacity(allocator, capacity) };
|
||||
}
|
||||
|
||||
pub fn initCopy(allocator: *std.mem.Allocator, str: anytype) !MutableString {
|
||||
var mutable = try MutableString.init(allocator, std.mem.len(str));
|
||||
try mutable.copy(str);
|
||||
return mutable;
|
||||
}
|
||||
|
||||
// Convert it to an ASCII identifier. Note: If you change this to a non-ASCII
|
||||
// identifier, you're going to potentially cause trouble with non-BMP code
|
||||
// points in target environments that don't support bracketed Unicode escapes.
|
||||
|
||||
pub fn ensureValidIdentifier(str: string, allocator: *std.mem.Allocator) !string {
|
||||
if (str.len == 0) {
|
||||
return "_";
|
||||
}
|
||||
|
||||
var mutable = try MutableString.init(allocator, 0);
|
||||
|
||||
var needsGap = false;
|
||||
for (str) |c| {
|
||||
if (std.ascii.isLower(c) or std.ascii.isUpper(c) or (mutable.len() > 0 and std.ascii.isAlNum(c))) {
|
||||
if (needsGap) {
|
||||
try mutable.appendChar('_');
|
||||
needsGap = false;
|
||||
}
|
||||
try mutable.appendChar(c);
|
||||
} else if (!needsGap) {
|
||||
needsGap = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mutable.len() > 0) {
|
||||
return mutable.list.toOwnedSlice(allocator);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(self: *MutableString) usize {
|
||||
return self.list.items.len;
|
||||
}
|
||||
|
||||
pub fn copy(self: *MutableString, str: anytype) !void {
|
||||
try self.list.ensureCapacity(self.allocator, std.mem.len(str[0..]));
|
||||
|
||||
if (self.list.items.len == 0) {
|
||||
try self.list.insertSlice(self.allocator, 0, str);
|
||||
} else {
|
||||
try self.list.replaceRange(self.allocator, 0, std.mem.len(str[0..]), str[0..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *MutableString) !void {
|
||||
self.list.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn appendChar(self: *MutableString, char: u8) !void {
|
||||
try self.list.append(self.allocator, char);
|
||||
}
|
||||
|
||||
pub fn appendCharAssumeCapacity(self: *MutableString, char: u8) !void {
|
||||
try self.list.appendAssumeCapacity(self.allocator, char);
|
||||
}
|
||||
|
||||
pub fn append(self: *MutableString, char: []const u8) !void {
|
||||
try self.list.appendSlice(self.allocator, char);
|
||||
}
|
||||
|
||||
pub fn appendAssumeCapacity(self: *MutableString, char: []const u8) !void {
|
||||
try self.list.appendSliceAssumeCapacity(self.allocator, char);
|
||||
}
|
||||
|
||||
// pub fn deleteAt(self: *MutableString, i: usize) {
|
||||
// self.list.swapRemove(i);
|
||||
// }
|
||||
|
||||
pub fn containsChar(self: *MutableString, char: u8) bool {
|
||||
return self.indexOfChar(char) != null;
|
||||
}
|
||||
|
||||
pub fn indexOfChar(self: *MutableString, char: u8) ?usize {
|
||||
return std.mem.indexOfScalar(@TypeOf(char), self.list.items, char);
|
||||
}
|
||||
|
||||
pub fn lastIndexOfChar(self: *MutableString, char: u8) ?usize {
|
||||
return std.mem.lastIndexOfScalar(@TypeOf(char), self.list.items, char);
|
||||
}
|
||||
|
||||
pub fn lastIndexOf(self: *MutableString, str: u8) ?usize {
|
||||
return std.mem.lastIndexOf(u8, self.list.items, str);
|
||||
}
|
||||
|
||||
pub fn indexOf(self: *MutableString, str: u8) ?usize {
|
||||
return std.mem.indexOf(u8, self.list.items, str);
|
||||
}
|
||||
|
||||
pub fn eql(self: *MutableString, other: anytype) bool {
|
||||
return std.mem.eql(u8, self.list.items, other);
|
||||
}
|
||||
};
|
||||
|
||||
test "MutableString" {
|
||||
const alloc = std.heap.page_allocator;
|
||||
|
||||
var str = try MutableString.initCopy(alloc, "hello");
|
||||
expect(str.eql("hello"));
|
||||
}
|
||||
|
||||
test "MutableString.ensureValidIdentifier" {
|
||||
const alloc = std.heap.page_allocator;
|
||||
|
||||
std.testing.expectEqualStrings("jquery", try MutableString.ensureValidIdentifier("jquery", alloc));
|
||||
std.testing.expectEqualStrings("jquery_foo", try MutableString.ensureValidIdentifier("jquery😋foo", alloc));
|
||||
}
|
||||
2
src/string_types.zig
Normal file
2
src/string_types.zig
Normal file
@@ -0,0 +1,2 @@
|
||||
pub const string = []const u8;
|
||||
pub const stringMutable = []u8;
|
||||
@@ -1,8 +1,7 @@
|
||||
const std = @import("std");
|
||||
pub fn indexOfChar(contents: []u8, char: u8) callconv(.Inline) ?usize {
|
||||
return std.mem.indexOfScalar(u8, contents, char);
|
||||
}
|
||||
const mutable = @import("string_mutable.zig");
|
||||
|
||||
pub fn lastIndexOfChar(contents: []u8, char: u8) callconv(.Inline) ?usize {
|
||||
return std.mem.lastIndexOfScalar(u8, contents, char);
|
||||
}
|
||||
pub usingnamespace @import("string_types.zig");
|
||||
|
||||
pub const strings = @import("string_immutable.zig");
|
||||
|
||||
pub const MutableString = mutable.MutableString;
|
||||
|
||||
Reference in New Issue
Block a user