newrpl/newrpl-comp.c
Stefan Achatz 13e277fab9 Header cleanup
Removing unneeded includes and adding needed includes to make headers self contained.
Decimating double and degraded type definitions.
Added missing variables to position dependent block.
Conditional inclusion of headers to reduce, albeit not prevent, recursive inclusion of open files.
2020-04-07 09:09:53 +02:00

448 lines
14 KiB
C

/*
* Copyright (c) 2014-2015, Claudio Lapilli and the newRPL Team
* All rights reserved.
* This file is released under the 3-clause BSD license.
* See the file LICENSE.txt that shipped with this distribution.
*/
#include "cmdcodes.h"
#include "libraries.h"
#include "ui.h"
#include <stdio.h>
#include <string.h>
extern void lib4079_handler();
// DISPLAY AN ERROR MESSAGE
// USES ERROR CODE FROM SYSTEM Exceptions
// OUTPUTS THE ERROR TO THE GIVEN STREAM (USUALLY stderr)
void compShowErrorMsg(char *inputfile, char *mainbuffer, FILE * stream)
{
int errbit;
if(!Exceptions)
return;
char *position = (char *)TokenStart;
char *linestart = NULL;
// COMPUTE LINE NUMBER
int linenum = 1;
while(position > mainbuffer) {
--position;
if(*position == '\n') {
++linenum;
if(!linestart)
linestart = position + 1;
}
}
// COUNT CHARACTERS FROM START OF LINE
position = (char *)TokenStart;
while(*linestart == '\r')
++linestart;
int posnum = utf8nlen(linestart, position) + 1;
fprintf(stream, "%s:%d:%d:", inputfile, linenum, posnum);
if(Exceptions != EX_ERRORCODE) {
if(ExceptionPointer && (*ExceptionPointer != 0)) // ONLY IF THERE'S A VALID COMMAND TO BLAME
{
WORDPTR cmdname = halGetCommandName(ExceptionPointer);
if(cmdname) {
BYTEPTR start = (BYTEPTR) (cmdname + 1);
BYTEPTR end = start + rplStrSize(cmdname);
fwrite(start, 1, end - start, stream);
}
}
fprintf(stream, " Exception: ");
BINT ecode;
for(errbit = 0; errbit < 8; ++errbit) // THERE'S ONLY A FEW EXCEPTIONS IN THE NEW ERROR MODEL
{
if(Exceptions & (1 << errbit)) {
ecode = MAKEMSG(0, errbit);
WORDPTR message = uiGetLibMsg(ecode);
fwrite((char *)(message + 1), 1, rplStrSize(message), stream);
break;
}
}
}
else {
// TRY TO DECOMPILE THE OPCODE THAT CAUSED THE ERROR
if(ExceptionPointer && (*ExceptionPointer != 0)) // ONLY IF THERE'S A VALID COMMAND TO BLAME
{
WORDPTR cmdname = halGetCommandName(ExceptionPointer);
if(cmdname) {
BYTEPTR start = (BYTEPTR) (cmdname + 1);
BYTEPTR end = start + rplStrSize(cmdname);
fwrite(start, 1, end - start, stream);
}
}
fprintf(stream, " error: ");
// TODO: GET NEW TRANSLATABLE MESSAGES
WORDPTR message = uiGetLibMsg(ErrorCode);
if(!message)
fprintf(stream, " %d in library %d\n", ErrorCode & 0x7f,
LIBFROMMSG(ErrorCode));
else
fwrite((char *)(message + 1), 1, rplStrSize(message), stream);
}
fprintf(stream, "\n");
}
enum
{
OUTPUT_BINARY,
OUTPUT_C
};
int main(int argc, char *argv[])
{
char *mainbuffer;
if(argc < 2) {
printf("NewRPL standalone compiler - Version 1.0\n");
printf("Usage: newrpl-comp [-c] [-o <outputfile>] <filename.nrpl>\n");
printf("\nOptions:\n");
printf("\t\t-c\tOutput will be as C source code.\n");
printf("\t\t-o <file>\tSpecify a output file name (defaults to filename.c or filename.binrpl)\n\n\n");
return 0;
}
int argidx = 1;
int outputtype = OUTPUT_BINARY;
int needoutputname = 0;
int needcleanup = 0;
char *outputfile = NULL;
char *inputfile = NULL;
while(argidx < argc) {
if(needoutputname) {
outputfile = argv[argidx];
needoutputname = 0;
}
else if((argv[argidx][0] == '-') && (argv[argidx][1] == 'c')
&& (argv[argidx][2] == 0))
outputtype = OUTPUT_C;
else if((argv[argidx][0] == '-') && (argv[argidx][1] == 'o')) {
if(argv[argidx][2] == 0)
needoutputname = 1;
else
outputfile = argv[argidx] + 2;
}
else
inputfile = argv[argidx];
++argidx;
}
// HERE WE HAVE ALL ARGUMENTS PROCESSED
if(!inputfile) {
fprintf(stderr, "Error: No input file\n");
return 1;
}
if(!outputfile) {
// CREATE AN OUTPUT FILE NAME FROM THE INPUT FILE
char *end = inputfile + strlen(inputfile) - 1;
while((end > inputfile) && (*end != '.') && (*end != '/')
&& (*end != '\\'))
--end;
if(end <= inputfile)
end = inputfile + strlen(inputfile);
else if(*end != '.')
end = inputfile + strlen(inputfile);
needcleanup++;
outputfile = malloc(end - inputfile + 10);
if(!outputfile) {
fprintf(stderr, "error: Memory allocation error\n");
return 1;
}
memmove(outputfile, inputfile, end - inputfile);
strcpy(outputfile + (end - inputfile),
(outputtype == OUTPUT_C) ? ".c" : ".binrpl");
}
// READ THE INPUT FILE INTO A BUFFER
FILE *f = fopen(inputfile, "rb");
if(f == NULL) {
fprintf(stderr, "error: File not found %s\n", inputfile);
if(needcleanup)
free(outputfile);
return 1;
}
fseek(f, 0, SEEK_END);
long long length = ftell(f);
fseek(f, 0, SEEK_SET);
mainbuffer = malloc(length);
if(!mainbuffer) {
fprintf(stderr, "error: Memory allocation error\n");
if(needcleanup)
free(outputfile);
return 1;
}
if(fread(mainbuffer, 1, length, f) != (size_t)length) {
fprintf(stderr, "error: Can't read from input file\n");
if(needcleanup)
free(outputfile);
free(mainbuffer);
return 1;
}
fclose(f);
// HERE WE HAVE THE MAIN FILE
rplInitMemoryAllocator();
rplInit();
rplSetSystemFlag(FL_STRIPCOMMENTS);
rplInstallLibrary(lib4079_handler);
// IDENTIFY CHUNKS OF CODE
char *chunk = mainbuffer;
int numchunks = 0;
char *chunkstart[65537]; // MAXIMUM NUMBER OF VARIABLES IN A SINGLE FILE
while(chunk - mainbuffer < length) {
if(!utf8ncmp2(chunk, mainbuffer + length, "@#name", 6)) {
// FOUND START OF CHUNK
chunkstart[numchunks] = chunk;
++numchunks;
if(numchunks > 65535) {
fprintf(stderr, "error: Too many chunks in same file.\n");
if(needcleanup)
free(outputfile);
free(mainbuffer);
return 1;
}
}
//SKIP TO THE NEXT LINE
while((*chunk != '\n') && (*chunk != '\r')
&& (chunk - mainbuffer < length))
++chunk;
while(((*chunk == '\n') || (*chunk == '\r'))
&& (chunk - mainbuffer < length))
++chunk;
}
if(numchunks == 0) {
chunkstart[numchunks] = mainbuffer;
++numchunks;
}
chunkstart[numchunks] = chunk;
f = fopen(outputfile, "wb");
if(f == NULL) {
fprintf(stderr, "error: Can't open %s for writing.\n", outputfile);
if(needcleanup)
free(outputfile);
free(mainbuffer);
return 1;
}
if(outputtype == OUTPUT_BINARY) {
// WRITE THE BINARY FILE MARKER
WORD marker[3] = {
MKPROLOG(HEXBINT, 2), // PROLOG OF A 64-BIT BINT
0x4c50526e, // STRING "nRPL"
1 // VERSION OF THE newRPL BINARY FORMAT
};
fwrite(marker, 4, 3, f);
}
else {
fprintf(f, "// newRPL binary version 1.0\n\n");
fprintf(f, "#include \"cmdcodes.h\"\n");
fprintf(f, "#include \"libraries.h\"\n");
fprintf(f, "#include \"newrpl.h\"\n");
fprintf(f, "#include \"firmware.h\"\n\n");
}
// COMPILE ALL CHUNKS
int k;
char *start, *end;
for(k = 0; k < numchunks; ++k) {
start = chunkstart[k];
end = chunkstart[k + 1];
if(!utf8ncmp2(start, end, "@#name", 6)) {
// SKIP TO THE NEXT LINE FOR THE REAL DATA
while((*start != '\n') && (*start != '\r') && (start < end))
++start;
while(((*start == '\n') || (*start == '\r')) && (start < end))
++start;
}
if(end > start) {
WORDPTR newobject = rplCompile((BYTEPTR) start, end - start, 1);
if(Exceptions) {
compShowErrorMsg(inputfile, mainbuffer, stderr);
fclose(f);
remove(outputfile);
if(needcleanup)
free(outputfile);
free(mainbuffer);
return 1;
}
// OUTPUT THE CHUNK
if(outputtype == OUTPUT_C) {
if(rplObjSize(newobject) > 2) {
// OUTPUT C FORMATTED CODE
char *objname;
char *nameend;
if(!utf8ncmp2(chunkstart[k], chunkstart[k + 1], "@#name",
6)) {
objname = chunkstart[k] + 6;
while((*objname == ' ') || (*objname == '\t'))
++objname;
nameend = objname;
while((*nameend != '\n') && (*nameend != '\r')
&& (*nameend != ' ') && (*nameend != '\t'))
++nameend;
}
else {
nameend = objname = NULL;
}
fprintf(f, "ROMOBJECT ");
if(nameend <= objname)
fprintf(f, "chunk%05d", k + 1);
else
fwrite(objname, 1, nameend - objname, f);
fprintf(f, "[]= {\n");
WORDPTR p = newobject + 1, endp = rplSkipOb(newobject) - 1;
int wordcount = 0;
while(p < endp) {
if(LIBNUM(*p) == 4079) {
// THIS IS A PSEUDO-OBJECT, NEEDS TO BE REPLACED WITH TEXT
int textoffset;
int givenwords, storedwords;
if(ISPROLOG(*p)) {
textoffset = p[1];
storedwords = OBJSIZE(*p) + 1;
}
else {
textoffset = OBJSIZE(*p);
storedwords = 1;
}
if(textoffset > 0) {
char *foundtext = end - textoffset, *endtext;
if(foundtext >= start) {
// IT'S A VALID POINTER INTO THE TEXT!
// FIND THE END OF THE TEXT
givenwords = 1;
endtext = foundtext;
while(endtext < end) {
if(*endtext == ',')
++givenwords;
if(*endtext == ' ')
break;
if(*endtext == '\t')
break;
if(*endtext == '\n')
break;
if(*endtext == '\r')
break;
++endtext;
}
// REMOVED THIS CHECK TO ALLOW FOR C MACROS THAT EXPAND TO MORE THAN ONE WORD
//if(givenwords==storedwords) {
// IT'S A VALID SPECIAL OBJECT
fwrite(foundtext, 1, endtext - foundtext,
f);
p += storedwords;
if(p != endp)
fprintf(f, ",");
if((wordcount & 7) + storedwords > 7)
fprintf(f, "\n");
wordcount += storedwords;
continue;
//}
}
rplError(ERR_SYNTAXERROR);
TokenStart = (WORDPTR) foundtext;
compShowErrorMsg(inputfile, mainbuffer, stderr);
}
// IF IT FALLS THROUGH, THEN IT'S AN INVALID SPECIAL SEQUENCE
// JUST CONTINUE NORMALLY, AS IT COULD BE RANDOM DATA
}
fprintf(f, "0x%08X", *p);
if(p != endp - 1)
fprintf(f, ",");
wordcount++;
if((wordcount & 7) == 0)
fprintf(f, "\n");
++p;
}
if((wordcount & 7) != 0)
fprintf(f, "\n");
fprintf(f, "};\n\n\n");
}
}
else {
if(rplObjSize(newobject) > 2) {
// RAW BINARY OUTPUT
fwrite(newobject + 1, 4, rplObjSize(newobject) - 2, f);
}
}
}
// AND MOVE ON TO THE NEXT ONE
}
// CLOSE THE OUTPUT FILE
fclose(f);
return 0;
}
void usb_mutex_lock_implementation(void)
{}
void usb_mutex_unlock_implementation(void)
{}