Implement header token parsing
This commit is contained in:
parent
01600013b9
commit
8b27c7385f
2
LICENSE
2
LICENSE
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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,
|
||||
}));
|
||||
}
|
||||
Loading…
Reference in New Issue