Abstract the file reading
This commit is contained in:
parent
aa6b2504bd
commit
473cd4e498
@ -3,7 +3,333 @@
|
|||||||
*/
|
*/
|
||||||
module elna.extended;
|
module elna.extended;
|
||||||
|
|
||||||
|
import core.stdc.errno;
|
||||||
|
import core.stdc.stdio;
|
||||||
|
import std.sumtype;
|
||||||
|
import std.typecons;
|
||||||
|
import tanya.os.error;
|
||||||
|
import tanya.container.array;
|
||||||
|
import tanya.container.string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File handle abstraction.
|
||||||
|
*/
|
||||||
struct File
|
struct File
|
||||||
{
|
{
|
||||||
@disable this(this);
|
/// Plattform dependent file type.
|
||||||
|
alias Handle = FILE*;
|
||||||
|
|
||||||
|
/// Uninitialized file handle value.
|
||||||
|
enum Handle invalid = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relative position.
|
||||||
|
*/
|
||||||
|
enum Whence
|
||||||
|
{
|
||||||
|
/// Relative to the start of the file.
|
||||||
|
set = SEEK_SET,
|
||||||
|
/// Relative to the current cursor position.
|
||||||
|
currentt = SEEK_CUR,
|
||||||
|
/// Relative from the end of the file.
|
||||||
|
end = SEEK_END,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File open modes.
|
||||||
|
*/
|
||||||
|
enum Mode
|
||||||
|
{
|
||||||
|
/// Open the file for reading.
|
||||||
|
read = 1 << 0,
|
||||||
|
/// Open the file for writing. The stream is positioned at the beginning
|
||||||
|
/// of the file.
|
||||||
|
write = 1 << 1,
|
||||||
|
/// Open the file for writing and remove its contents.
|
||||||
|
truncate = 1 << 2,
|
||||||
|
/// Open the file for writing. The stream is positioned at the end of
|
||||||
|
/// the file.
|
||||||
|
append = 1 << 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Status
|
||||||
|
{
|
||||||
|
invalid,
|
||||||
|
owned,
|
||||||
|
borrowed,
|
||||||
|
}
|
||||||
|
|
||||||
|
private union Storage
|
||||||
|
{
|
||||||
|
Handle handle;
|
||||||
|
ErrorCode errorCode;
|
||||||
|
}
|
||||||
|
private Storage storage;
|
||||||
|
private Status status = Status.invalid;
|
||||||
|
|
||||||
|
@disable this(scope return ref File f);
|
||||||
|
@disable this();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the file.
|
||||||
|
*/
|
||||||
|
~this() @nogc nothrow
|
||||||
|
{
|
||||||
|
if (this.status == Status.owned)
|
||||||
|
{
|
||||||
|
fclose(this.storage.handle);
|
||||||
|
}
|
||||||
|
this.storage.handle = invalid;
|
||||||
|
this.status = Status.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the object with the given system handle. The won't be claused
|
||||||
|
* in the descructor if this constructor is used.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* handle = File handle to be wrapped by this structure.
|
||||||
|
*/
|
||||||
|
this(Handle handle) @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
this.storage.handle = handle;
|
||||||
|
this.status = Status.borrowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Plattform dependent file handle.
|
||||||
|
*/
|
||||||
|
@property Handle handle() @nogc nothrow pure @trusted
|
||||||
|
{
|
||||||
|
return valid ? this.storage.handle : invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: An error code if an error has occurred.
|
||||||
|
*/
|
||||||
|
@property ErrorCode errorCode() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return valid ? ErrorCode() : this.storage.errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Whether a valid, opened file is represented.
|
||||||
|
*/
|
||||||
|
@property bool valid() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this.status != Status.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfers the file into invalid state.
|
||||||
|
*
|
||||||
|
* Returns: The old file handle.
|
||||||
|
*/
|
||||||
|
Handle reset() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
auto oldHandle = handle;
|
||||||
|
|
||||||
|
this.status = Status.invalid;
|
||||||
|
this.storage.errorCode = ErrorCode();
|
||||||
|
|
||||||
|
return oldHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets stream position in the file.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* offset = File offset.
|
||||||
|
* whence = File position to add the offset to.
|
||||||
|
*
|
||||||
|
* Returns: Error code if any.
|
||||||
|
*/
|
||||||
|
ErrorCode seek(size_t offset, Whence whence) @nogc nothrow
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return ErrorCode(ErrorCode.ErrorNo.badDescriptor);
|
||||||
|
}
|
||||||
|
if (fseek(this.storage.handle, offset, whence))
|
||||||
|
{
|
||||||
|
return ErrorCode(cast(ErrorCode.ErrorNo) errno);
|
||||||
|
}
|
||||||
|
return ErrorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Current offset or an error.
|
||||||
|
*/
|
||||||
|
SumType!(ErrorCode, size_t) tell() @nogc nothrow
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||||
|
}
|
||||||
|
auto result = ftell(this.storage.handle);
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
return typeof(return)(ErrorCode(cast(ErrorCode.ErrorNo) errno));
|
||||||
|
}
|
||||||
|
return typeof(return)(cast(size_t) result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* buffer = Destination buffer.
|
||||||
|
*
|
||||||
|
* Returns: Bytes read. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that
|
||||||
|
* while reading the file an unknown error has occurred.
|
||||||
|
*/
|
||||||
|
SumType!(ErrorCode, size_t) read(ubyte[] buffer) @nogc nothrow
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||||
|
}
|
||||||
|
const bytesRead = fread(buffer.ptr, 1, buffer.length, this.storage.handle);
|
||||||
|
if (bytesRead == buffer.length || eof())
|
||||||
|
{
|
||||||
|
return typeof(return)(bytesRead);
|
||||||
|
}
|
||||||
|
return typeof(return)(ErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* buffer = Source buffer.
|
||||||
|
*
|
||||||
|
* Returns: Bytes written. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that
|
||||||
|
* while reading the file an unknown error has occurred.
|
||||||
|
*/
|
||||||
|
SumType!(ErrorCode, size_t) write(const(ubyte)[] buffer) @nogc nothrow
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||||
|
}
|
||||||
|
const bytesWritten = fwrite(buffer.ptr, 1, buffer.length, this.storage.handle);
|
||||||
|
if (bytesWritten == buffer.length)
|
||||||
|
{
|
||||||
|
return typeof(return)(buffer.length);
|
||||||
|
}
|
||||||
|
return typeof(return)(ErrorCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: EOF status of the file.
|
||||||
|
*/
|
||||||
|
bool eof() @nogc nothrow
|
||||||
|
{
|
||||||
|
return valid && feof(this.storage.handle) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a file object that will be closed in the destructor.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* filename = The file to open.
|
||||||
|
*
|
||||||
|
* Returns: Opened file or an error.
|
||||||
|
*/
|
||||||
|
static File open(const(char)* filename, BitFlags!Mode mode) @nogc nothrow
|
||||||
|
{
|
||||||
|
char[3] modeBuffer = "\0\0\0";
|
||||||
|
|
||||||
|
if (mode.truncate)
|
||||||
|
{
|
||||||
|
modeBuffer[0] = 'w';
|
||||||
|
if (mode.read)
|
||||||
|
{
|
||||||
|
modeBuffer[1] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode.append)
|
||||||
|
{
|
||||||
|
modeBuffer[0] = 'a';
|
||||||
|
if (mode.read)
|
||||||
|
{
|
||||||
|
modeBuffer[1] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode.read)
|
||||||
|
{
|
||||||
|
modeBuffer[0] = 'r';
|
||||||
|
if (mode.write)
|
||||||
|
{
|
||||||
|
modeBuffer[1] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newHandle = fopen(filename, modeBuffer.ptr);
|
||||||
|
auto newFile = File(newHandle);
|
||||||
|
|
||||||
|
if (newHandle is null)
|
||||||
|
{
|
||||||
|
newFile.status = Status.invalid;
|
||||||
|
newFile.storage.errorCode = ErrorCode(cast(ErrorCode.ErrorNo) errno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mode == BitFlags!Mode(Mode.write))
|
||||||
|
{
|
||||||
|
rewind(newHandle);
|
||||||
|
}
|
||||||
|
newFile.status = Status.owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the whole file and returns its contents.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* sourceFilename = Source filename.
|
||||||
|
*
|
||||||
|
* Returns: File contents or an error.
|
||||||
|
*
|
||||||
|
* See_Also: $(D_PSYMBOL File.read)
|
||||||
|
*/
|
||||||
|
SumType!(ErrorCode, Array!ubyte) readFile(String sourceFilename) @nogc
|
||||||
|
{
|
||||||
|
enum size_t bufferSize = 255;
|
||||||
|
auto sourceFile = File.open(sourceFilename.toStringz, BitFlags!(File.Mode)(File.Mode.read));
|
||||||
|
|
||||||
|
if (!sourceFile.valid)
|
||||||
|
{
|
||||||
|
return typeof(return)(sourceFile.errorCode);
|
||||||
|
}
|
||||||
|
Array!ubyte sourceText;
|
||||||
|
size_t totalRead;
|
||||||
|
size_t bytesRead;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sourceText.length = sourceText.length + bufferSize;
|
||||||
|
const readStatus = sourceFile
|
||||||
|
.read(sourceText[totalRead .. $].get)
|
||||||
|
.match!(
|
||||||
|
(ErrorCode errorCode) => nullable(errorCode),
|
||||||
|
(size_t bytesRead_) {
|
||||||
|
bytesRead = bytesRead_;
|
||||||
|
return Nullable!ErrorCode();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!readStatus.isNull)
|
||||||
|
{
|
||||||
|
return typeof(return)(readStatus.get);
|
||||||
|
}
|
||||||
|
totalRead += bytesRead;
|
||||||
|
}
|
||||||
|
while (bytesRead == bufferSize);
|
||||||
|
|
||||||
|
sourceText.length = totalRead;
|
||||||
|
|
||||||
|
return typeof(return)(sourceText);
|
||||||
}
|
}
|
||||||
|
@ -5,47 +5,43 @@ import elna.lexer;
|
|||||||
import elna.parser;
|
import elna.parser;
|
||||||
import elna.generator;
|
import elna.generator;
|
||||||
import elna.ir;
|
import elna.ir;
|
||||||
|
import elna.extended;
|
||||||
|
import std.sumtype;
|
||||||
|
import std.typecons;
|
||||||
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
import tanya.memory.allocator;
|
import tanya.memory.allocator;
|
||||||
import tanya.memory.mmappool;
|
import tanya.memory.mmappool;
|
||||||
|
import tanya.os.error;
|
||||||
|
|
||||||
private char[] readSource(size_t N)(string source, out char[N] buffer) @nogc
|
private Nullable!String readSource(string source) @nogc
|
||||||
{
|
{
|
||||||
memcpy(buffer.ptr, source.ptr, source.length + 1);
|
enum size_t bufferSize = 255;
|
||||||
buffer[source.length] = '\0';
|
auto sourceFilename = String(source);
|
||||||
auto handle = fopen(buffer.ptr, "r");
|
|
||||||
if (handle is null)
|
|
||||||
{
|
|
||||||
perror(buffer.ptr);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
fseek(handle, 0, SEEK_END);
|
|
||||||
size_t fsize = ftell(handle);
|
|
||||||
rewind(handle);
|
|
||||||
|
|
||||||
fread(buffer.ptr, fsize, 1, handle);
|
return readFile(sourceFilename).match!(
|
||||||
fclose(handle);
|
(ErrorCode errorCode) {
|
||||||
buffer[fsize] = '\0';
|
perror(sourceFilename.toStringz);
|
||||||
|
return Nullable!String();
|
||||||
return buffer[0 .. fsize];
|
},
|
||||||
|
(Array!ubyte contents) => nullable(String(cast(char[]) contents.get))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(string[] args)
|
int main(string[] args)
|
||||||
{
|
{
|
||||||
char[255] buffer;
|
|
||||||
|
|
||||||
defaultAllocator = MmapPool.instance;
|
defaultAllocator = MmapPool.instance;
|
||||||
|
|
||||||
if (args.length < 2)
|
if (args.length < 2)
|
||||||
{
|
{
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
auto sourceText = readSource(args[1], buffer);
|
auto sourceText = readSource(args[1]);
|
||||||
if (sourceText is null)
|
if (sourceText.isNull)
|
||||||
{
|
{
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
auto tokens = lex(sourceText);
|
auto tokens = lex(sourceText.get.get);
|
||||||
if (tokens.length == 0)
|
if (tokens.length == 0)
|
||||||
{
|
{
|
||||||
printf("Lexical analysis failed.\n");
|
printf("Lexical analysis failed.\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user