Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix compiler crash related to @Alignof
- Loading branch information
Showing
5 changed files
with
267 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const Builder = @import("std").build.Builder; | ||
|
||
pub fn build(b: &Builder) -> %void { | ||
const main = b.addTest("main.zig"); | ||
main.setBuildMode(b.standardReleaseOptions()); | ||
|
||
const test_step = b.step("test", "Test it"); | ||
test_step.dependOn(&main.step); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
const std = @import("std"); | ||
const io = std.io; | ||
const mem = std.mem; | ||
const debug = std.debug; | ||
const assert = debug.assert; | ||
const Buffer = std.Buffer; | ||
const ArrayList = std.ArrayList; | ||
|
||
error InvalidInput; | ||
error OutOfMem; | ||
|
||
const Token = union(enum) { | ||
Word: []const u8, | ||
OpenBrace, | ||
CloseBrace, | ||
Comma, | ||
Eof, | ||
}; | ||
|
||
var global_allocator: &mem.Allocator = undefined; | ||
|
||
fn tokenize(input:[] const u8) -> %ArrayList(Token) { | ||
const State = enum { | ||
Start, | ||
Word, | ||
}; | ||
|
||
var token_list = ArrayList(Token).init(global_allocator); | ||
var tok_begin: usize = undefined; | ||
var state = State.Start; | ||
|
||
for (input) |b, i| { | ||
switch (state) { | ||
State.Start => switch (b) { | ||
'a'...'z', 'A'...'Z' => { | ||
state = State.Word; | ||
tok_begin = i; | ||
}, | ||
'{' => try token_list.append(Token.OpenBrace), | ||
'}' => try token_list.append(Token.CloseBrace), | ||
',' => try token_list.append(Token.Comma), | ||
else => return error.InvalidInput, | ||
}, | ||
State.Word => switch (b) { | ||
'a'...'z', 'A'...'Z' => {}, | ||
'{', '}', ',' => { | ||
try token_list.append(Token { .Word = input[tok_begin..i] }); | ||
switch (b) { | ||
'{' => try token_list.append(Token.OpenBrace), | ||
'}' => try token_list.append(Token.CloseBrace), | ||
',' => try token_list.append(Token.Comma), | ||
else => unreachable, | ||
} | ||
state = State.Start; | ||
}, | ||
else => return error.InvalidInput, | ||
}, | ||
} | ||
} | ||
switch (state) { | ||
State.Start => {}, | ||
State.Word => try token_list.append(Token {.Word = input[tok_begin..] }), | ||
} | ||
try token_list.append(Token.Eof); | ||
return token_list; | ||
} | ||
|
||
const Node = union(enum) { | ||
Scalar: []const u8, | ||
List: ArrayList(Node), | ||
Combine: []Node, | ||
}; | ||
|
||
fn parse(tokens: &const ArrayList(Token), token_index: &usize) -> %Node { | ||
const first_token = tokens.items[*token_index]; | ||
*token_index += 1; | ||
|
||
const result_node = switch (first_token) { | ||
Token.Word => |word| Node { .Scalar = word }, | ||
Token.OpenBrace => blk: { | ||
var list = ArrayList(Node).init(global_allocator); | ||
while (true) { | ||
try list.append(try parse(tokens, token_index)); | ||
|
||
const token = tokens.items[*token_index]; | ||
*token_index += 1; | ||
|
||
switch (token) { | ||
Token.CloseBrace => break, | ||
Token.Comma => continue, | ||
else => return error.InvalidInput, | ||
} | ||
} | ||
break :blk Node { .List = list }; | ||
}, | ||
else => return error.InvalidInput, | ||
}; | ||
|
||
switch (tokens.items[*token_index]) { | ||
Token.Word, Token.OpenBrace => { | ||
const pair = try global_allocator.alloc(Node, 2); | ||
pair[0] = result_node; | ||
pair[1] = try parse(tokens, token_index); | ||
return Node { .Combine = pair }; | ||
}, | ||
else => return result_node, | ||
} | ||
} | ||
|
||
fn expandString(input: []const u8, output: &Buffer) -> %void { | ||
const tokens = try tokenize(input); | ||
if (tokens.len == 1) { | ||
return output.resize(0); | ||
} | ||
|
||
var token_index: usize = 0; | ||
const root = try parse(tokens, &token_index); | ||
const last_token = tokens.items[token_index]; | ||
switch (last_token) { | ||
Token.Eof => {}, | ||
else => return error.InvalidInput, | ||
} | ||
|
||
var result_list = ArrayList(Buffer).init(global_allocator); | ||
defer result_list.deinit(); | ||
|
||
try expandNode(root, &result_list); | ||
|
||
try output.resize(0); | ||
for (result_list.toSliceConst()) |buf, i| { | ||
if (i != 0) { | ||
try output.appendByte(' '); | ||
} | ||
try output.append(buf.toSliceConst()); | ||
} | ||
} | ||
|
||
const ListOfBuffer0 = ArrayList(Buffer); // TODO this is working around a compiler bug, fix and delete this | ||
|
||
fn expandNode(node: &const Node, output: &ListOfBuffer0) -> %void { | ||
assert(output.len == 0); | ||
switch (*node) { | ||
Node.Scalar => |scalar| { | ||
try output.append(try Buffer.init(global_allocator, scalar)); | ||
}, | ||
Node.Combine => |pair| { | ||
const a_node = pair[0]; | ||
const b_node = pair[1]; | ||
|
||
var child_list_a = ArrayList(Buffer).init(global_allocator); | ||
try expandNode(a_node, &child_list_a); | ||
|
||
var child_list_b = ArrayList(Buffer).init(global_allocator); | ||
try expandNode(b_node, &child_list_b); | ||
|
||
for (child_list_a.toSliceConst()) |buf_a| { | ||
for (child_list_b.toSliceConst()) |buf_b| { | ||
var combined_buf = try Buffer.initFromBuffer(buf_a); | ||
try combined_buf.append(buf_b.toSliceConst()); | ||
try output.append(combined_buf); | ||
} | ||
} | ||
}, | ||
Node.List => |list| { | ||
for (list.toSliceConst()) |child_node| { | ||
var child_list = ArrayList(Buffer).init(global_allocator); | ||
try expandNode(child_node, &child_list); | ||
|
||
for (child_list.toSliceConst()) |buf| { | ||
try output.append(buf); | ||
} | ||
} | ||
}, | ||
} | ||
} | ||
|
||
pub fn main() -> %void { | ||
var stdin_file = try io.getStdIn(); | ||
var stdout_file = try io.getStdOut(); | ||
|
||
var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024); | ||
defer inc_allocator.deinit(); | ||
|
||
global_allocator = &inc_allocator.allocator; | ||
|
||
var stdin_buf = try Buffer.initSize(global_allocator, 0); | ||
defer stdin_buf.deinit(); | ||
|
||
var stdin_adapter = io.FileInStream.init(&stdin_file); | ||
try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize)); | ||
|
||
var result_buf = try Buffer.initSize(global_allocator, 0); | ||
defer result_buf.deinit(); | ||
|
||
try expandString(stdin_buf.toSlice(), &result_buf); | ||
try stdout_file.write(result_buf.toSliceConst()); | ||
} | ||
|
||
test "invalid inputs" { | ||
global_allocator = std.debug.global_allocator; | ||
|
||
expectError("}ABC", error.InvalidInput); | ||
expectError("{ABC", error.InvalidInput); | ||
expectError("}{", error.InvalidInput); | ||
expectError("{}", error.InvalidInput); | ||
expectError("A,B,C", error.InvalidInput); | ||
expectError("{A{B,C}", error.InvalidInput); | ||
expectError("{A,}", error.InvalidInput); | ||
|
||
expectError("\n", error.InvalidInput); | ||
} | ||
|
||
fn expectError(test_input: []const u8, expected_err: error) { | ||
var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable; | ||
defer output_buf.deinit(); | ||
|
||
if (expandString("}ABC", &output_buf)) { | ||
unreachable; | ||
} else |err| { | ||
assert(expected_err == err); | ||
} | ||
} | ||
|
||
test "valid inputs" { | ||
global_allocator = std.debug.global_allocator; | ||
|
||
expectExpansion("{x,y,z}", "x y z"); | ||
expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); | ||
expectExpansion("{A,B{x,y}}", "A Bx By"); | ||
|
||
expectExpansion("{ABC}", "ABC"); | ||
expectExpansion("{A,B,C}", "A B C"); | ||
expectExpansion("ABC", "ABC"); | ||
|
||
expectExpansion("", ""); | ||
expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); | ||
expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); | ||
expectExpansion("{A,B}a", "Aa Ba"); | ||
expectExpansion("{C,{x,y}}", "C x y"); | ||
expectExpansion("z{C,{x,y}}", "zC zx zy"); | ||
expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); | ||
expectExpansion("a{x,y}b", "axb ayb"); | ||
expectExpansion("z{{a,b}}", "za zb"); | ||
expectExpansion("a{b}", "ab"); | ||
} | ||
|
||
fn expectExpansion(test_input: []const u8, expected_result: []const u8) { | ||
var result = Buffer.initSize(global_allocator, 0) catch unreachable; | ||
defer result.deinit(); | ||
|
||
expandString(test_input, &result) catch unreachable; | ||
|
||
assert(mem.eql(u8, result.toSlice(), expected_result)); | ||
} |