1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-20 15:29:10 +00:00

Added javadoc to #pragmas.

This commit is contained in:
jespergravgaard 2021-12-30 14:29:58 +01:00
parent 9652931592
commit 385794ae99

View File

@ -2,10 +2,11 @@ package dk.camelot64.kickc.parser;
import dk.camelot64.kickc.FileNameUtils; import dk.camelot64.kickc.FileNameUtils;
import dk.camelot64.kickc.SourceLoader; import dk.camelot64.kickc.SourceLoader;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.TargetPlatform;
import dk.camelot64.kickc.model.statements.StatementSource; import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.StringEncoding;
import dk.camelot64.kickc.passes.Pass4CodeGeneration;
import dk.camelot64.kickc.preprocessor.CPreprocessor; import dk.camelot64.kickc.preprocessor.CPreprocessor;
import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.*;
@ -24,327 +25,407 @@ import java.util.*;
public class CParser { public class CParser {
/** The hidden lexer channel containing whitespace. */ /**
public static final int CHANNEL_WHITESPACE = 1; * The hidden lexer channel containing whitespace.
/** The hidden lexer channel containing comments. */ */
public static final int CHANNEL_COMMENTS = 2; public static final int CHANNEL_WHITESPACE = 1;
/**
* The hidden lexer channel containing comments.
*/
public static final int CHANNEL_COMMENTS = 2;
/** Pragma Names. */ /**
public static final String PRAGMA_TARGET = "target"; * #pragma target(...) selects the target platform to compile for.
public static final String PRAGMA_CPU = "cpu"; * See {@link TargetPlatform}.
public static final String PRAGMA_VAR_MODEL = "var_model"; */
public static final String PRAGMA_LINKSCRIPT = "link"; public static final String PRAGMA_TARGET = "target";
public static final String PRAGMA_EXTENSION = "extension"; /**
public static final String PRAGMA_EMULATOR = "emulator"; * #pragma cpu(...) selects which CPU to compile for. See {@link TargetCpu}.
public static final String PRAGMA_ENCODING = "encoding"; */
public static final String PRAGMA_CODE_SEG = "code_seg"; public static final String PRAGMA_CPU = "cpu";
public static final String PRAGMA_DATA_SEG = "data_seg"; /**
public static final String PRAGMA_START_ADDRESS = "start_address"; * #pragma var_model(...) configures how different variables should be compiled.
public static final String PRAGMA_CALLING = "calling"; * See {@link VariableBuilderConfig}.
public static final String PRAGMA_ZP_RESERVE = "zp_reserve"; */
public static final String PRAGMA_CONSTRUCTOR_FOR = "constructor_for"; public static final String PRAGMA_VAR_MODEL = "var_model";
public static final String PRAGMA_INTERRUPT = "interrupt"; /**
public static final String PRAGMA_STRUCT_MODEL = "struct_model"; * #pragma link("filename") selects a custom linker file.
public static final String PRAGMA_RESOURCE = "resource"; * See {@link Pass4CodeGeneration#generate()}
*/
public static final String PRAGMA_LINKSCRIPT = "link";
/**
* #pragma extension(...) specifies the extension of the generated binary output file.
*/
public static final String PRAGMA_EXTENSION = "extension";
/**
* #pragma emulator(...) specifies which emulator to run when executing the binary program.
*/
public static final String PRAGMA_EMULATOR = "emulator";
/**
* #pragma encoding(...) specifies which character encoding to use for strings and chars.
* See {@link StringEncoding}.
*/
public static final String PRAGMA_ENCODING = "encoding";
/**
* #pragma code_seg(...) changes the current code segment. Segments are defined in the linker file.
*/
public static final String PRAGMA_CODE_SEG = "code_seg";
/**
* #pragma data_seg(...) changes the current data segment. Segments are defined in the linker file.
*/
public static final String PRAGMA_DATA_SEG = "data_seg";
/**
* #pragma start_address(...) specifies the start address of the program.
*/
public static final String PRAGMA_START_ADDRESS = "start_address";
/**
* #pragma calling(...) specifies which default calling convention to use for functions.
* See {@link Procedure.CallingConvention}.
*/
public static final String PRAGMA_CALLING = "calling";
/**
* #pragma zp_reserve(...) specifies that some zeropage-addresses should be reserved preventing the
* compiler from allocating variables to those addresses.
*/
public static final String PRAGMA_ZP_RESERVE = "zp_reserve";
/**
* #pragma constructor_for(...) specifies that a certain function is an
* initializer for a library and must be called before main().
*/
public static final String PRAGMA_CONSTRUCTOR_FOR = "constructor_for";
/**
* #pragma interrupt(...) specifies the default interrupt type to use for functions marked with __interrupt.
*/
public static final String PRAGMA_INTERRUPT = "interrupt";
/**
* #pragma struct_model(...) sepcifies which struct model to use by default.
*/
public static final String PRAGMA_STRUCT_MODEL = "struct_model";
/**
* #pragma resource("filename") specifies that a specific file is a resource that is needed for assembly.
* The resource-file is copied to the output directory when compiling.
*/
public static final String PRAGMA_RESOURCE = "resource";
/** The Program. */ /**
private Program program; * The Program.
*/
private Program program;
/** The (single) parser. */ /**
private KickCParser parser; * The (single) parser.
*/
private KickCParser parser;
/** The preprocessor. */ /**
private CPreprocessor preprocessor; * The preprocessor.
*/
private CPreprocessor preprocessor;
/** The token stream. */ /**
private final CommonTokenStream preprocessedTokenStream; * The token stream.
*/
private final CommonTokenStream preprocessedTokenStream;
/** The token source stack handling import files. */ /**
private CTokenSource cTokenSource; * The token source stack handling import files.
*/
private CTokenSource cTokenSource;
/** The input files that have been parsed. Maps file name to the lexer. */ /**
private Map<String, CFile> cFiles; * The input files that have been parsed. Maps file name to the lexer.
*/
private Map<String, CFile> cFiles;
/** Names of typedefs. Used by lexer to know the difference between normal value IDENTIFIERS and TYPEIDENTIFIERS */ /**
private List<String> typedefs; * Names of typedefs. Used by lexer to know the difference between normal value IDENTIFIERS and TYPEIDENTIFIERS
*/
private List<String> typedefs;
/** A C-file that has been imported & parsed. */ /**
public static class CFile { * A C-file that has been imported & parsed.
/** The source file currently being parsed. */ */
private File file; public static class CFile {
/** The lexer. */ /**
private KickCLexer lexer; * The source file currently being parsed.
*/
private File file;
/**
* The lexer.
*/
private KickCLexer lexer;
CFile(File file, KickCLexer lexer) { CFile(File file, KickCLexer lexer) {
this.file = file; this.file = file;
this.lexer = lexer; this.lexer = lexer;
} }
} }
public CParser(Program program) { public CParser(Program program) {
this.program = program; this.program = program;
this.cFiles = new LinkedHashMap<>(); this.cFiles = new LinkedHashMap<>();
this.cTokenSource = new CTokenSource(); this.cTokenSource = new CTokenSource();
this.preprocessor = new CPreprocessor(this, cTokenSource, new HashMap<>()); this.preprocessor = new CPreprocessor(this, cTokenSource, new HashMap<>());
this.preprocessedTokenStream = new CommonTokenStream(preprocessor); this.preprocessedTokenStream = new CommonTokenStream(preprocessor);
this.parser = new KickCParser(preprocessedTokenStream, this); this.parser = new KickCParser(preprocessedTokenStream, this);
this.typedefs = new ArrayList<>(); this.typedefs = new ArrayList<>();
parser.setBuildParseTree(true); parser.setBuildParseTree(true);
parser.addErrorListener(new BaseErrorListener() { parser.addErrorListener(new BaseErrorListener() {
@Override @Override
public void syntaxError( public void syntaxError(
Recognizer<?, ?> recognizer, Recognizer<?, ?> recognizer,
Object offendingSymbol, Object offendingSymbol,
int line, int line,
int charPositionInLine, int charPositionInLine,
String msg, String msg,
RecognitionException e) { RecognitionException e) {
final Token offendingToken = (Token) offendingSymbol; final Token offendingToken = (Token) offendingSymbol;
StatementSource source = new StatementSource(offendingToken.getInputStream().getSourceName(), line, charPositionInLine, null, -1, -1); StatementSource source = new StatementSource(offendingToken.getInputStream().getSourceName(), line, charPositionInLine, null, -1, -1);
throw new CompileError("Error parsing file: " + msg, source); throw new CompileError("Error parsing file: " + msg, source);
} }
}); });
} }
public void addTypedef(String identifier) { public void addTypedef(String identifier) {
typedefs.add(identifier); typedefs.add(identifier);
} }
public boolean isTypedef(String identifier) { public boolean isTypedef(String identifier) {
return typedefs.contains(identifier); return typedefs.contains(identifier);
} }
/** /**
* Get the preprocessor (usable for getting all preprocessed tokens). * Get the preprocessor (usable for getting all preprocessed tokens).
* *
* @return The preprocessor * @return The preprocessor
*/ */
public CPreprocessor getPreprocessor() { public CPreprocessor getPreprocessor() {
return preprocessor; return preprocessor;
} }
/** /**
* Define a new macro * Define a new macro
* *
* @param macroName The macro name * @param macroName The macro name
* @param macroBody The macro body * @param macroBody The macro body
*/ */
public void define(String macroName, String macroBody) { public void define(String macroName, String macroBody) {
final String macroBodyText = "#define " + macroName + " " + macroBody + "\n"; final String macroBodyText = "#define " + macroName + " " + macroBody + "\n";
final CodePointCharStream macroCharStream = CharStreams.fromString(macroBodyText); final CodePointCharStream macroCharStream = CharStreams.fromString(macroBodyText);
final KickCLexer macroLexer = makeLexer(macroCharStream); final KickCLexer macroLexer = makeLexer(macroCharStream);
addSourceFirst(macroLexer); addSourceFirst(macroLexer);
} }
/** /**
* Undef a macro * Undef a macro
* *
* @param macroName The macro name * @param macroName The macro name
*/ */
public void undef(String macroName) { public void undef(String macroName) {
getPreprocessor().undef(macroName); getPreprocessor().undef(macroName);
} }
/** /**
* Get the token stream containing tokens after the preprocessor. * Get the token stream containing tokens after the preprocessor.
* *
* @return The preprocessed token stream * @return The preprocessed token stream
*/ */
public BufferedTokenStream getPreprocessedTokenStream() { public BufferedTokenStream getPreprocessedTokenStream() {
return preprocessedTokenStream; return preprocessedTokenStream;
} }
/** /**
* Get the C parser * Get the C parser
* *
* @return The C parser * @return The C parser
*/ */
public KickCParser getParser() { public KickCParser getParser() {
return parser; return parser;
} }
/** /**
* Get the path of the folder containing the source file for a token * Get the path of the folder containing the source file for a token
* *
* @param context The source context to examine * @param context The source context to examine
* @return The path of the folder containing the source file of the token * @return The path of the folder containing the source file of the token
*/ */
public Path getSourceFolderPath(ParserRuleContext context) { public Path getSourceFolderPath(ParserRuleContext context) {
Token token = context.getStart(); Token token = context.getStart();
String sourceName = token.getTokenSource().getSourceName(); String sourceName = token.getTokenSource().getSourceName();
CFile cFile = cFiles.get(sourceName); CFile cFile = cFiles.get(sourceName);
File parentFile = cFile.file.getParentFile(); File parentFile = cFile.file.getParentFile();
return parentFile.toPath(); return parentFile.toPath();
} }
/** /**
* Get the path of the folder containing the source file currently being tokenized * Get the path of the folder containing the source file currently being tokenized
* *
* @return The path of the folder containing the source file currently being tokenized * @return The path of the folder containing the source file currently being tokenized
*/ */
public Path getCurrentSourceFolderPath() { public Path getCurrentSourceFolderPath() {
TokenSource currentSource = cTokenSource.getCurrentSource(); TokenSource currentSource = cTokenSource.getCurrentSource();
String sourceName = currentSource.getSourceName(); String sourceName = currentSource.getSourceName();
CFile cFile = cFiles.get(sourceName); CFile cFile = cFiles.get(sourceName);
File file = cFile.file; File file = cFile.file;
File parentFile = file.getParentFile(); File parentFile = file.getParentFile();
return parentFile.toPath(); return parentFile.toPath();
} }
/** /**
* Loads a C-file (if it has not already been loaded). * Loads a C-file (if it has not already been loaded).
* <p> * <p>
* The C-file is inserted into the C token stream at the current parse-point - so the parser will parse the entire content of the file before moving on. * The C-file is inserted into the C token stream at the current parse-point - so the parser will parse the entire content of the file before moving on.
* *
* @param fileName The file name of the file * @param fileName The file name of the file
*/ */
public void includeCFile(String fileName, boolean isSystem) { public void includeCFile(String fileName, boolean isSystem) {
if(fileName.startsWith("\"") || fileName.startsWith("<")) { if (fileName.startsWith("\"") || fileName.startsWith("<")) {
fileName = fileName.substring(1, fileName.length() - 1); fileName = fileName.substring(1, fileName.length() - 1);
} }
final Path currentSourceFolderPath = isSystem ? null : getCurrentSourceFolderPath(); final Path currentSourceFolderPath = isSystem ? null : getCurrentSourceFolderPath();
final TokenSource fileTokens = loadCFile(fileName, currentSourceFolderPath, program.getIncludePaths(), false); final TokenSource fileTokens = loadCFile(fileName, currentSourceFolderPath, program.getIncludePaths(), false);
if(fileName.endsWith(".h")) { if (fileName.endsWith(".h")) {
// The included file was a H-file - attempt to find the matching library C-file // The included file was a H-file - attempt to find the matching library C-file
String libFileName = FileNameUtils.removeExtension(fileName) + ".c"; String libFileName = FileNameUtils.removeExtension(fileName) + ".c";
final TokenSource cLibFileTokens = loadCFile(libFileName, currentSourceFolderPath, program.getLibraryPaths(), true); final TokenSource cLibFileTokens = loadCFile(libFileName, currentSourceFolderPath, program.getLibraryPaths(), true);
addSourceFirst(cLibFileTokens); addSourceFirst(cLibFileTokens);
} }
addSourceFirst(fileTokens); addSourceFirst(fileTokens);
} }
/** /**
* Loads a C-file (if it has not already been loaded). * Loads a C-file (if it has not already been loaded).
* *
* @param fileName The file name of the file * @param fileName The file name of the file
* @param currentPath The path of the current folder (searched before the search path). * @param currentPath The path of the current folder (searched before the search path).
* @param searchPaths The folders to look in if the files is not found in current path * @param searchPaths The folders to look in if the files is not found in current path
* @return The lexer tokens to be inserted into the source-list using one of the {@link #addSourceFirst(TokenSource)} / {@link #addSourceLast(TokenSource)} methods. * @return The lexer tokens to be inserted into the source-list using one of the {@link #addSourceFirst(TokenSource)} / {@link #addSourceLast(TokenSource)} methods.
*/ */
public TokenSource loadCFile(String fileName, Path currentPath, List<String> searchPaths, boolean acceptFileNotFound) { public TokenSource loadCFile(String fileName, Path currentPath, List<String> searchPaths, boolean acceptFileNotFound) {
try { try {
File file = SourceLoader.loadFile(fileName, currentPath, searchPaths); File file = SourceLoader.loadFile(fileName, currentPath, searchPaths);
if(file == null) if (file == null)
if(acceptFileNotFound) if (acceptFileNotFound)
return null; return null;
else else
throw new CompileError("File not found " + fileName); throw new CompileError("File not found " + fileName);
Path filePath = file.toPath().toAbsolutePath().normalize(); Path filePath = file.toPath().toAbsolutePath().normalize();
List<String> included = program.getLoadedFiles(); List<String> included = program.getLoadedFiles();
if(included.contains(filePath.toString())) { if (included.contains(filePath.toString())) {
return null; return null;
} }
final CharStream fileStream = CharStreams.fromPath(filePath); final CharStream fileStream = CharStreams.fromPath(filePath);
included.add(filePath.toString()); included.add(filePath.toString());
if(program.getLog().isVerboseParse()) { if (program.getLog().isVerboseParse()) {
program.getLog().append("PARSING " + filePath.toString().replace("\\", "/")); program.getLog().append("PARSING " + filePath.toString().replace("\\", "/"));
program.getLog().append(fileStream.toString()); program.getLog().append(fileStream.toString());
} }
KickCLexer lexer = makeLexer(fileStream); KickCLexer lexer = makeLexer(fileStream);
CFile cFile = new CFile(file, lexer); CFile cFile = new CFile(file, lexer);
cFiles.put(filePath.toString(), cFile); cFiles.put(filePath.toString(), cFile);
return lexer; return lexer;
} catch(IOException e) { } catch (IOException e) {
throw new CompileError("Error parsing file " + fileName, e); throw new CompileError("Error parsing file " + fileName, e);
} }
} }
/** /**
* Update the target platform * Update the target platform
* *
* @param platformName The name of the platform * @param platformName The name of the platform
* @param currentPath The current path * @param currentPath The current path
*/ */
public void updateTargetPlatform(String platformName, Path currentPath) { public void updateTargetPlatform(String platformName, Path currentPath) {
final TargetPlatform targetPlatform = loadPlatformFile(platformName, currentPath, program.getTargetPlatformPaths()); final TargetPlatform targetPlatform = loadPlatformFile(platformName, currentPath, program.getTargetPlatformPaths());
// Remove macros from existing platform! // Remove macros from existing platform!
if(program.getTargetPlatform() != null && program.getTargetPlatform().getDefines() != null) if (program.getTargetPlatform() != null && program.getTargetPlatform().getDefines() != null)
for(String macroName : program.getTargetPlatform().getDefines().keySet()) for (String macroName : program.getTargetPlatform().getDefines().keySet())
preprocessor.undef(macroName); preprocessor.undef(macroName);
// Remove reserved ZP's from existing platform // Remove reserved ZP's from existing platform
program.getReservedZps().removeAll(program.getTargetPlatform().getReservedZps()); program.getReservedZps().removeAll(program.getTargetPlatform().getReservedZps());
// Set the new program platform // Set the new program platform
program.setTargetPlatform(targetPlatform); program.setTargetPlatform(targetPlatform);
// Define macros from new platform! // Define macros from new platform!
if(program.getTargetPlatform().getDefines() != null) if (program.getTargetPlatform().getDefines() != null)
for(String macroName : program.getTargetPlatform().getDefines().keySet()) for (String macroName : program.getTargetPlatform().getDefines().keySet())
define(macroName, program.getTargetPlatform().getDefines().get(macroName)); define(macroName, program.getTargetPlatform().getDefines().get(macroName));
// Add reserved ZP's from new platform // Add reserved ZP's from new platform
program.addReservedZps(program.getTargetPlatform().getReservedZps()); program.addReservedZps(program.getTargetPlatform().getReservedZps());
// Set the output file extension // Set the output file extension
program.getOutputFileManager().setBinaryExtension(targetPlatform.getOutFileExtension()); program.getOutputFileManager().setBinaryExtension(targetPlatform.getOutFileExtension());
} }
/** /**
* Load a platform file and produce an error if it cannot be found. * Load a platform file and produce an error if it cannot be found.
* @param platformName The platform name *
* @param currentPath The current path * @param platformName The platform name
* @param targetPlatformPaths The search list of paths to look through * @param currentPath The current path
* @return The loaded platform file * @param targetPlatformPaths The search list of paths to look through
*/ * @return The loaded platform file
public static TargetPlatform loadPlatformFile(String platformName, Path currentPath, List<String> targetPlatformPaths) { */
final File platformFile = SourceLoader.loadFile(platformName + "." + CTargetPlatformParser.FILE_EXTENSION, currentPath, targetPlatformPaths); public static TargetPlatform loadPlatformFile(String platformName, Path currentPath, List<String> targetPlatformPaths) {
if(platformFile == null) { final File platformFile = SourceLoader.loadFile(platformName + "." + CTargetPlatformParser.FILE_EXTENSION, currentPath, targetPlatformPaths);
StringBuilder supported = new StringBuilder(); if (platformFile == null) {
final List<File> platformFiles = SourceLoader.listFiles(currentPath, targetPlatformPaths, CTargetPlatformParser.FILE_EXTENSION); StringBuilder supported = new StringBuilder();
for(File file : platformFiles) { final List<File> platformFiles = SourceLoader.listFiles(currentPath, targetPlatformPaths, CTargetPlatformParser.FILE_EXTENSION);
String name = file.getName(); for (File file : platformFiles) {
name = name.substring(0, name.length() - CTargetPlatformParser.FILE_EXTENSION.length() - 1); String name = file.getName();
supported.append(name).append(" "); name = name.substring(0, name.length() - CTargetPlatformParser.FILE_EXTENSION.length() - 1);
} supported.append(name).append(" ");
throw new CompileError("Unknown target platform '" + platformName + "'. Supported: " + supported); }
} throw new CompileError("Unknown target platform '" + platformName + "'. Supported: " + supported);
return CTargetPlatformParser.parseTargetPlatformFile(platformName, platformFile, currentPath, targetPlatformPaths); }
} return CTargetPlatformParser.parseTargetPlatformFile(platformName, platformFile, currentPath, targetPlatformPaths);
}
/** /**
* Make a lexer for a char stream * Make a lexer for a char stream
* *
* @param charStream the char stream * @param charStream the char stream
* @return the lexer * @return the lexer
*/ */
public KickCLexer makeLexer(CharStream charStream) { public KickCLexer makeLexer(CharStream charStream) {
KickCLexer lexer = new KickCLexer(charStream, this); KickCLexer lexer = new KickCLexer(charStream, this);
lexer.addErrorListener(new BaseErrorListener() { lexer.addErrorListener(new BaseErrorListener() {
@Override @Override
public void syntaxError( public void syntaxError(
Recognizer<?, ?> recognizer, Recognizer<?, ?> recognizer,
Object offendingSymbol, Object offendingSymbol,
int line, int line,
int charPositionInLine, int charPositionInLine,
String msg, String msg,
RecognitionException e) { RecognitionException e) {
StatementSource source = new StatementSource(charStream.getSourceName(), line, charPositionInLine, null, -1, -1); StatementSource source = new StatementSource(charStream.getSourceName(), line, charPositionInLine, null, -1, -1);
throw new CompileError("Error parsing file: " + msg, source); throw new CompileError("Error parsing file: " + msg, source);
} }
}); });
return lexer; return lexer;
} }
/** /**
* Add source code at the start of the token stream being parsed. * Add source code at the start of the token stream being parsed.
* *
* @param tokenSource The lexer for reading the source tokens * @param tokenSource The lexer for reading the source tokens
*/ */
public void addSourceFirst(TokenSource tokenSource) { public void addSourceFirst(TokenSource tokenSource) {
if(tokenSource != null) if (tokenSource != null)
cTokenSource.addSourceFirst(tokenSource); cTokenSource.addSourceFirst(tokenSource);
} }
/** /**
* Add source code at the end of the token stream being parsed. * Add source code at the end of the token stream being parsed.
* *
* @param tokenSource The lexer for reading the source tokens * @param tokenSource The lexer for reading the source tokens
*/ */
public void addSourceLast(TokenSource tokenSource) { public void addSourceLast(TokenSource tokenSource) {
if(tokenSource != null) if (tokenSource != null)
cTokenSource.addSourceLast(tokenSource); cTokenSource.addSourceLast(tokenSource);
} }
} }