ref: 206275df57a65d6aeabaac9a0a4cb102eb333407
parent: 3d8396b86f4d95cb0fd76ac1418c0d92e296b84f
author: Antonio Niño Díaz <[email protected]>
date: Sat Apr 8 14:10:24 EDT 2017
Add support for including files in linkerscript Files can now be included with the following syntax: INCLUDE "path.link" The maximum include depth is 5. Fixed linkerscript parser and lexer error messages so that they are more informative (show file and line of the error). Man page updated. Signed-off-by: Antonio Niño Díaz <[email protected]>
--- a/include/link/script.h
+++ b/include/link/script.h
@@ -17,7 +17,15 @@
#ifndef RGBDS_LINK_SCRIPT_H
#define RGBDS_LINK_SCRIPT_H
+#include "extern/stdnoreturn.h"
+
+noreturn void script_fatalerror(const char *fmt, ...);
+
void script_Parse(const char *path);
+
+void script_IncludeFile(const char *path);
+int script_IncludeDepthGet(void);
+void script_IncludePop(void);
void script_InitSections(void);
void script_SetCurrentSectionType(const char *type, unsigned int bank);
--- a/src/link/assign.c
+++ b/src/link/assign.c
@@ -445,6 +445,7 @@
*/
if (tzLinkerscriptName) {
+ script_InitSections();
script_Parse(tzLinkerscriptName);
}
--- a/src/link/lexer.l
+++ b/src/link/lexer.l
@@ -16,13 +16,29 @@
%option noinput
%option nounput
+%option yylineno
%{
+#include <stdarg.h>
#include <unistd.h>
#include "extern/err.h"
+#include "link/mylink.h"
+#include "link/script.h"
#include "parser.h"
+
+/* File include stack. */
+
+#define MAX_INCLUDE_DEPTH 8
+
+static int include_stack_ptr = 0;
+
+static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+static char include_path[MAX_INCLUDE_DEPTH][_MAX_PATH + 1];
+static int include_line[MAX_INCLUDE_DEPTH];
+
+static char linkerscript_path[_MAX_PATH + 1]; /* Base file */
%}
%%
@@ -29,9 +45,9 @@
\"([^\\\"]|\\.)*\" {
if (strlen(yytext) > sizeof(yylval.s) - 1)
- errx(1, "String is too long: \"%s\"\n.", yytext);
+ script_fatalerror("String is too long: %s\n.", yytext);
if (strlen(yytext) < 3) /* 2 quotes + 1 character */
- errx(1, "String \"%s\" is invalid\n.", yytext);
+ script_fatalerror("String %s is invalid\n.", yytext);
yytext++; /* ignore first quote */
strcpy(yylval.s, yytext);
@@ -62,6 +78,8 @@
(?i:ALIGN) { return COMMAND_ALIGN; }
(?i:ORG) { return COMMAND_ORG; }
+(?i:INCLUDE) { return COMMAND_INCLUDE; }
+
"\n" { return NEWLINE; }
;.* { /* Ignore comments. A dot doesn't match newline. */ }
@@ -68,7 +86,91 @@
[[:space:]] { /* Ignore whitespace. */ }
-. { errx(1, "Invalid character [%s]\n.", yytext); }
+. { script_fatalerror("Invalid character [%s]\n.", yytext); }
%%
+
+extern FILE *yyin;
+
+void script_Parse(const char * path)
+{
+ yyin = fopen(path, "r");
+
+ if (!yyin)
+ errx(1, "Error opening file! \"%s\"\n", path);
+
+ strncpy(linkerscript_path, path, sizeof(linkerscript_path));
+ linkerscript_path[sizeof(linkerscript_path) - 1] = '\0';
+
+ do {
+ yyparse();
+ } while (!feof(yyin));
+
+ fclose(yyin);
+
+}
+
+void script_IncludeFile(const char * path)
+{
+ if (include_stack_ptr == (MAX_INCLUDE_DEPTH-1))
+ script_fatalerror("Includes nested too deeply.");
+
+ include_line[include_stack_ptr] = yylineno;
+ include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
+
+ include_stack_ptr++;
+
+ yyin = fopen(path, "r" );
+
+ if (!yyin)
+ script_fatalerror("Couldn't open file \"%s\"", path);
+
+ strncpy(include_path[include_stack_ptr], path, sizeof(include_path[0]));
+ include_path[include_stack_ptr][sizeof(include_path[0])-1] = '\0';
+
+ yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+ yylineno = 0;
+}
+
+int script_IncludeDepthGet(void)
+{
+ return include_stack_ptr;
+}
+
+void script_IncludePop(void)
+{
+ fclose(yyin);
+
+ include_stack_ptr--;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ yy_switch_to_buffer(include_stack[include_stack_ptr]);
+ yylineno = include_line[include_stack_ptr];
+}
+
+void script_PrintFileStack(void)
+{
+ int i = include_stack_ptr;
+
+ include_line[i] = yylineno;
+
+ while (i > 0) {
+ fprintf(stderr, "%s(%d) -> ", include_path[i], include_line[i]);
+ i--;
+ }
+ fprintf(stderr, "%s(%d)", linkerscript_path, include_line[i]);
+}
+
+noreturn void script_fatalerror(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "error: ");
+ script_PrintFileStack();
+ fprintf(stderr, ":\n\t");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
--- a/src/link/parser.y
+++ b/src/link/parser.y
@@ -23,7 +23,7 @@
int yylex();
void yyerror(char *);
-static int nline = 1;
+extern int yylineno;
%}
%union { int i; char s[512]; }
@@ -37,6 +37,8 @@
%token COMMAND_ALIGN
%token COMMAND_ORG
+%token COMMAND_INCLUDE
+
%token NEWLINE
%start lines
@@ -49,8 +51,8 @@
;
line:
- /* empty */ { nline++; }
- | statement { nline++; }
+ /* empty */
+ | statement
;
statement:
@@ -59,11 +61,11 @@
script_SetCurrentSectionType($1, 0);
}
| SECTION_NONBANKED INTEGER {
- errx(1, "%d:Trying to assign a bank to a non-banked section.\n", nline);
+ script_fatalerror("Trying to assign a bank to a non-banked section.\n");
}
| SECTION_BANKED {
- errx(1, "%d:Banked section without assigned bank.\n", nline);
+ script_fatalerror("Banked section without assigned bank.\n");
}
| SECTION_BANKED INTEGER {
script_SetCurrentSectionType($1, $2);
@@ -74,13 +76,13 @@
script_SetAlignment($2);
}
| COMMAND_ALIGN {
- errx(1, "%d:ALIGN keyword needs an argument.\n", nline);
+ script_fatalerror("ALIGN keyword needs an argument.\n");
}
| COMMAND_ORG INTEGER {
script_SetAddress($2);
}
| COMMAND_ORG {
- errx(1, "%d:ORG keyword needs an argument.\n", nline);
+ script_fatalerror("ORG keyword needs an argument.\n");
}
/* Section name */
@@ -88,6 +90,11 @@
script_OutputSection($1);
}
+ /* Include file */
+ | COMMAND_INCLUDE STRING {
+ script_IncludeFile($2);
+ }
+
/* End */
;
@@ -96,33 +103,18 @@
extern int yylex();
extern int yyparse();
-extern FILE *yyin;
-
-int yywrap (void)
+int yywrap(void)
{
- return 1;
+ if (script_IncludeDepthGet() == 0)
+ return 1;
+
+ script_IncludePop();
+
+ return 0;
}
void yyerror(char *s)
{
- errx(1, "%d:Linkerscript parse error: \"%s\"\n", nline, s);
-}
-
-void script_Parse(const char *path)
-{
- script_InitSections();
-
- FILE *f = fopen(path, "r");
-
- if (!f)
- errx(1, "Error opening file! \"%s\"\n", path);
-
- yyin = f;
-
- do {
- yyparse();
- } while (!feof(yyin));
-
- fclose(f);
+ script_fatalerror("Linkerscript parse error: \"%s\"\n", s);
}
--- a/src/link/rgblink.5
+++ b/src/link/rgblink.5
@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd March 27, 2017
+.Dd April 8, 2017
.Dt RGBLINK 5
.Os RGBDS Manual
.Sh NAME
@@ -34,35 +34,42 @@
that ends at the end of the line:
.Pp
ROMX $F ; This is a comment
- "Functions to read array"
- ALIGN 8
- "Array aligned to 256 bytes"
+ "Functions to read array"
+ ALIGN 8
+ "Array aligned to 256 bytes"
WRAMX 2
- "Some variables"
+ "Some variables"
.Pp
Numbers can be in decimal or hexadecimal format (the prefix is
.Ql $ ) .
It is an error if any bank or command is found before setting a bank.
.Pp
-The possible bank types are: ROM0, ROMX, VRAM, WRAM0, WRAMX, OAM and HRAM.
-Types ROMX, VRAM, WRAMX and SRAM are banked, which means that it is needed to
-specify a bank after the type.
+Files can be included by using the
+.Ar INCLUDE No keyword followed by a string with the path of the file that has
+to be included.
.Pp
+The possible bank types are:
+.Sy ROM0 , ROMX , VRAM , WRAM0 , WRAMX , OAM No and Sy HRAM .
+Types
+.Sy ROMX , VRAM , WRAMX No and Sy SRAM No are banked, which means that it is
+needed to specify a bank after the type.
+.Pp
When a new bank statement is found, sections found after it will be placed
right from the beginning of that bank.
If the linkerscript switches to a different bank and then it comes back to the
previous one it will continue from the last address that was used.
.Pp
-The only two commands are ORG and ALIGN:
+The only two commands are
+.Ar ORG No and Ar ALIGN :
.Bl -bullet
.It
-ORG sets the address in which new sections will be placed.
+.Ar ORG No sets the address in which new sections will be placed.
It can not be lower than the current address.
.It
-ALIGN will increase the address until it is aligned to the specified boundary
-(it tries to set to 0 the number of bits specified after the command: ALIGN 8
-will align to $100).
+.Ar ALIGN No will increase the address until it is aligned to the specified
+boundary (it tries to set to 0 the number of bits specified after the command:
+.Ar ALIGN No 8 No will align to No $100 ) .
.El
.Pp
Note: The bank, alignment, address and type of sections can be specified both