Implement header token parsing

This commit is contained in:
Enrico Lumetti 2021-05-29 22:04:19 +02:00
parent 01600013b9
commit 8b27c7385f
6 changed files with 270 additions and 1 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) <year> <owner> All rights reserved.
Copyright (c) 2021 Enrico Lumetti All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View File

@ -1,2 +1,4 @@
# zircon
Simple C11 compiler that uses ZIR as a target

27
build.zig Normal file
View File

@ -0,0 +1,27 @@
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("zircon", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}

14
src/main.zig Normal file
View File

@ -0,0 +1,14 @@
const std = @import("std");
const fs = std.fs;
const zircon = @import("zircon.zig");
pub fn main() anyerror!void {
var cwd_path = try fs.realpathAlloc(std.heap.page_allocator, ".");
defer std.heap.page_allocator.free(cwd_path);
std.log.info("Running in {s}", .{cwd_path});
var f = try fs.cwd().openFile("test.c", fs.File.OpenFlags{ .read = true });
defer f.close();
var pp = zircon.Preprocessor(fs.File.Reader).init(f.reader());
}

36
src/testUtil.zig Normal file
View File

@ -0,0 +1,36 @@
const zircon = @import("zircon.zig");
const std = @import("std");
pub const TestStringReaderError = error {
EndOfString,
OutOfBound,
};
pub const TestStringReader = struct {
source: []const u8,
pos: usize,
pub fn init(s: []const u8) TestStringReader {
return TestStringReader {
.source = s,
.pos = 0,
};
}
pub fn readByte(self: *TestStringReader) !u8 {
if (self.pos == self.source.len) {
return TestStringReaderError.EndOfString;
}
var b = self.source[self.pos];
self.pos += 1;
return b;
}
pub fn seekBack(self: *TestStringReader, n: usize) !void {
if (n > self.pos) {
return TestStringReaderError.OutOfBound;
}
self.pos -= n;
}
};

190
src/zircon.zig Normal file
View File

@ -0,0 +1,190 @@
const std = @import("std");
const fs = std.fs;
const SourceLoc = struct {
// source: []u8
line: usize,
col: usize,
abs: usize,
};
const PPToken = struct {
// A.1.1 (6.4)
const Type = enum {
hHeaderName,
qHeaderName,
identifier,
ppNumber,
characterConstant,
stringLiteral,
punctuator,
nonBlank,
};
start: SourceLoc,
end: SourceLoc,
kind: Type,
};
pub fn RewindableReader(comptime Seekable: type, comptime Reader: type, comptime getReaderFn: anytype, comptime seekByFn: anytype) type {
return struct {
const Self = @This();
reader: Reader,
source: Seekable,
pub fn readByte(self: Self) !u8 {
self.reader.readByte();
}
pub fn seekBack(n: usize) !void {
seekByFn(self.source, -n);
self.reader = getReaderFn(self.source);
}
};
}
pub fn rewindableReaderFromFile(f: fs.File) RewindableReader(fs.File, fs.Reader, fs.File.reader, fs.File.seekBy) {
return RewindableReader(fs.File, fs.Reader, fs.File.reader, fs.File.seekBy) {
.reader = f.reader(),
.source = f,
};
}
pub fn Preprocessor(comptime RReader: type) type {
return struct {
const Self = @This();
reader: RReader,
loc: SourceLoc,
lastRead: u8,
pub fn init(reader: RReader) Self {
return Self {
.reader = reader,
.lastRead = 0,
.loc = SourceLoc {
.line = 0,
.col = 0,
.abs = 0,
}
};
}
fn readByte(self: *Self) !u8 {
self.lastRead = try self.reader.readByte();
self.loc.abs += 1;
// TODO(enrico): support '\r\n' on windows?
if (self.lastRead == '\n') {
self.loc.col += 1;
self.loc.line = 0;
} else {
self.loc.col += 1;
}
return self.lastRead;
}
pub fn headerName(self: *Self) !?PPToken {
const rewindLoc = self.loc;
if (try self.headerNameDelim('<', '>')) |tok| {
return tok;
}
try self.rewindTo(rewindLoc);
if (try self.headerNameDelim('"', '"')) |tok| {
return tok;
}
return null;
}
fn headerNameDelim(self: *Self, startDelimiter: u8, endDelimiter: u8) !?PPToken {
const tokenStart = self.loc;
if (!try self.expectChar(startDelimiter)) {
return null;
}
while (true) {
// TODO(enrico): support '\r\n' on windows?
if (!try self.expectCharExceptAny(&.{'\n', endDelimiter})) {
break;
}
}
if (self.lastRead != endDelimiter) {
return null;
}
return PPToken {
.kind = if (startDelimiter == '"') PPToken.Type.qHeaderName else PPToken.Type.hHeaderName,
.start = tokenStart,
.end = self.loc,
};
}
fn rewindTo(self: *Self, loc: SourceLoc) !void {
const diff = self.loc.abs - loc.abs;
self.loc = loc;
try self.reader.seekBack(diff);
}
fn expectChar(self: *Self, c: u8) !bool {
var b = try self.readByte();
return b == c;
}
fn expectCharExceptAny(self: *Self, chars: []const u8) !bool {
var b = try self.readByte();
for (chars) |c| {
if (b == c) {
return false;
}
}
return true;
}
};
}
const testUtil = @import("testUtil.zig");
const expect = std.testing.expect;
test "h header name" {
var s = "<test.h>";
var reader = testUtil.TestStringReader.init(s[0..s.len]);
var pp = Preprocessor(testUtil.TestStringReader).init(reader);
var tok = try pp.headerName();
expect(std.meta.eql(tok.?, PPToken{
.start = SourceLoc{
.line = 0,
.col = 0,
.abs = 0,
},
.end = SourceLoc{
.line = 0,
.col = 8,
.abs = 8,
},
.kind = PPToken.Type.hHeaderName,
}));
}
test "q header name" {
var s = "\"test.h\"";
var reader = testUtil.TestStringReader.init(s[0..s.len]);
var pp = Preprocessor(testUtil.TestStringReader).init(reader);
var tok = try pp.headerName();
expect(std.meta.eql(tok.?, PPToken{
.start = SourceLoc{
.line = 0,
.col = 0,
.abs = 0,
},
.end = SourceLoc{
.line = 0,
.col = 8,
.abs = 8,
},
.kind = PPToken.Type.qHeaderName,
}));
}