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:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
# zircon
|
# zircon
|
||||||
|
|
||||||
|
Simple C11 compiler that uses ZIR as a target
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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