2017-07-18 16:10:12 +00:00
package dk.camelot64.kickc.passes ;
2017-05-07 18:32:30 +00:00
2020-08-23 22:35:48 +00:00
import dk.camelot64.cpufamily6502.CpuClobber ;
2023-12-12 07:20:06 +00:00
import dk.camelot64.kickc.FileNameUtils ;
2017-07-18 16:10:12 +00:00
import dk.camelot64.kickc.NumberParser ;
2019-08-24 22:20:50 +00:00
import dk.camelot64.kickc.SourceLoader ;
2023-12-12 07:20:06 +00:00
import dk.camelot64.kickc.asm.AsmExportLibrary ;
2024-01-01 15:50:19 +00:00
import dk.camelot64.kickc.asm.AsmImportLibrary ;
2023-11-18 09:40:50 +00:00
import dk.camelot64.kickc.asm.AsmLibrary ;
2020-03-27 20:08:18 +00:00
import dk.camelot64.kickc.model.InternalError ;
2019-12-22 10:53:37 +00:00
import dk.camelot64.kickc.model.* ;
2019-06-18 22:39:15 +00:00
import dk.camelot64.kickc.model.operators.* ;
2018-03-06 19:54:49 +00:00
import dk.camelot64.kickc.model.statements.* ;
import dk.camelot64.kickc.model.symbols.* ;
2020-04-30 20:15:59 +00:00
import dk.camelot64.kickc.model.types.* ;
2018-07-12 12:42:45 +00:00
import dk.camelot64.kickc.model.values.* ;
2019-08-29 20:52:58 +00:00
import dk.camelot64.kickc.parser.CParser ;
import dk.camelot64.kickc.parser.KickCParser ;
import dk.camelot64.kickc.parser.KickCParserBaseVisitor ;
2020-02-06 18:22:56 +00:00
import dk.camelot64.kickc.passes.utils.SizeOfConstants ;
2019-08-29 20:52:58 +00:00
import org.antlr.v4.runtime.BufferedTokenStream ;
import org.antlr.v4.runtime.ParserRuleContext ;
import org.antlr.v4.runtime.Token ;
2019-03-15 06:59:25 +00:00
import org.antlr.v4.runtime.tree.ParseTree ;
2017-05-07 18:32:30 +00:00
import org.antlr.v4.runtime.tree.TerminalNode ;
2018-07-07 17:23:38 +00:00
import java.io.File ;
2018-09-26 19:13:54 +00:00
import java.nio.file.Path ;
2023-12-12 07:20:06 +00:00
import java.nio.file.Paths ;
2019-03-14 23:02:33 +00:00
import java.util.* ;
2018-07-07 11:55:15 +00:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2020-02-25 21:15:39 +00:00
import java.util.stream.Collectors ;
2017-06-05 14:01:50 +00:00
/ * *
* Generates program SSA form by visiting the ANTLR4 parse tree
* /
2019-08-25 09:00:49 +00:00
public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor < Object > {
2017-05-07 18:32:30 +00:00
2019-08-24 22:20:50 +00:00
/** The C parser keeping track of C-files and lexers */
2020-12-21 00:04:34 +00:00
private final CParser cParser ;
2018-09-26 19:13:54 +00:00
/** The source ANTLR parse tree of the source file. */
2020-12-21 00:04:34 +00:00
private final KickCParser . FileContext fileCtx ;
2018-09-26 19:13:54 +00:00
/** The program containing all compile structures. */
2020-12-21 00:04:34 +00:00
private final Program program ;
2018-09-26 19:13:54 +00:00
/** Used to build the scopes of the source file. */
2020-12-21 00:04:34 +00:00
private final Stack < Scope > scopeStack ;
2020-08-23 22:35:48 +00:00
/** All #pragma constructor_for() statements. Collected during parsing and handled by {@link #generate()} before returning. */
2020-12-21 00:04:34 +00:00
private final List < KickCParser . PragmaContext > pragmaConstructorFors ;
2017-05-07 18:32:30 +00:00
2020-12-21 00:04:34 +00:00
public Pass0GenerateStatementSequence ( CParser cParser , KickCParser . FileContext fileCtx , Program program , Procedure . CallingConvention initialCallingConvention , StringEncoding defaultEncoding , String defaultInterruptType ) {
2019-08-24 22:20:50 +00:00
this . cParser = cParser ;
2018-09-26 19:13:54 +00:00
this . fileCtx = fileCtx ;
2017-12-02 15:35:13 +00:00
this . program = program ;
2017-06-11 17:15:09 +00:00
this . scopeStack = new Stack < > ( ) ;
2020-02-27 06:22:22 +00:00
this . currentCallingConvention = initialCallingConvention ;
2020-08-08 00:25:11 +00:00
this . currentEncoding = defaultEncoding ;
2023-04-06 20:46:28 +00:00
this . pragmaConstructorFors = new ArrayList < > ( ) ;
2020-12-21 00:04:34 +00:00
this . currentInterruptType = defaultInterruptType ;
2017-07-31 21:41:10 +00:00
scopeStack . push ( program . getScope ( ) ) ;
2017-05-07 18:32:30 +00:00
}
2019-03-31 15:38:21 +00:00
private Scope getCurrentScope ( ) {
2017-06-11 17:15:09 +00:00
return scopeStack . peek ( ) ;
2017-06-05 14:01:50 +00:00
}
2017-06-13 05:18:07 +00:00
private Procedure getCurrentProcedure ( ) {
2017-12-27 22:53:51 +00:00
for ( Scope scope : scopeStack ) {
if ( scope instanceof Procedure ) {
2017-06-13 05:18:07 +00:00
return ( Procedure ) scope ;
}
}
return null ;
}
2020-06-15 20:23:06 +00:00
/ * *
* Get the current procedure compilation
*
* @return The current procedure compilation
* /
private ProcedureCompilation getCurrentProcedureCompilation ( ) {
final Procedure currentProcedure = getCurrentProcedure ( ) ;
if ( currentProcedure = = null )
return null ;
else
return program . getProcedureCompilation ( currentProcedure . getRef ( ) ) ;
}
2020-06-21 08:23:04 +00:00
/ * *
* Add an intermediate variable to the current scope .
2020-06-21 22:26:44 +00:00
* < p >
2020-06-21 08:23:04 +00:00
* If the current scope is global the variable is added to the _init ( ) scope .
*
* @return The new intermediate variable
* /
private Variable addIntermediateVar ( ) {
Scope currentScope = getCurrentScope ( ) ;
2021-05-02 12:28:03 +00:00
if ( currentScope = = null | | ScopeRef . ROOT . equals ( currentScope . getRef ( ) ) ) {
2021-01-20 16:51:49 +00:00
currentScope = getInitProc ( ) ;
2020-06-21 08:23:04 +00:00
}
2021-06-06 10:19:05 +00:00
return VariableBuilder . createIntermediate ( currentScope , SymbolType . VAR , program ) ;
2020-06-21 08:23:04 +00:00
}
2020-06-15 20:23:06 +00:00
/ * *
* Add a statement to the current procedure .
* /
void addStatement ( Statement statement ) {
ProcedureCompilation procedureCompilation = getCurrentProcedureCompilation ( ) ;
2020-06-20 19:31:49 +00:00
if ( procedureCompilation = = null ) {
2021-01-20 16:51:49 +00:00
Procedure initProc = getInitProc ( ) ;
2020-06-15 20:23:06 +00:00
procedureCompilation = program . getProcedureCompilation ( initProc . getRef ( ) ) ;
}
final StatementSequence statementSequence = procedureCompilation . getStatementSequence ( ) ;
statementSequence . addStatement ( statement ) ;
}
2021-07-27 17:11:14 +00:00
Statement getPreviousStatement ( ) {
ProcedureCompilation procedureCompilation = getCurrentProcedureCompilation ( ) ;
if ( procedureCompilation = = null ) {
Procedure initProc = getInitProc ( ) ;
procedureCompilation = program . getProcedureCompilation ( initProc . getRef ( ) ) ;
}
final StatementSequence statementSequence = procedureCompilation . getStatementSequence ( ) ;
List < Statement > statements = statementSequence . getStatements ( ) ;
2021-08-08 11:47:48 +00:00
if ( statements . size ( ) = = 0 )
2021-07-27 17:11:14 +00:00
return null ;
else
2021-08-08 11:47:48 +00:00
return statements . get ( statements . size ( ) - 1 ) ;
2021-07-27 17:11:14 +00:00
}
2021-01-20 16:51:49 +00:00
private Procedure getInitProc ( ) {
// Statement outside procedure declaration - put into the _init procedure
Procedure initProc = program . getScope ( ) . getLocalProcedure ( SymbolRef . INIT_PROC_NAME ) ;
if ( initProc = = null ) {
// Create the _init() procedure
2023-04-23 20:04:23 +00:00
initProc = new Procedure ( SymbolRef . INIT_PROC_NAME , new SymbolTypeProcedure ( SymbolType . VOID , new ArrayList < > ( ) ) , program . getScope ( ) , Scope . SEGMENT_CODE_DEFAULT , Scope . SEGMENT_DATA_DEFAULT , Procedure . CallingConvention . PHI_CALL , Bank . COMMON ) ;
2021-01-20 16:51:49 +00:00
initProc . setDeclaredInline ( true ) ;
initProc . setParameters ( new ArrayList < > ( ) ) ;
program . getScope ( ) . add ( initProc ) ;
program . createProcedureCompilation ( initProc . getRef ( ) ) ;
2023-04-06 06:44:31 +00:00
program . getProcedureCompilation ( initProc . getRef ( ) ) . getStatementSequence ( ) . addStatement ( new StatementProcedureBegin ( initProc . getRef ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
2021-01-20 16:51:49 +00:00
}
return initProc ;
}
2018-09-26 19:13:54 +00:00
public void generate ( ) {
this . visit ( fileCtx ) ;
2020-06-18 08:01:45 +00:00
2020-08-23 22:35:48 +00:00
// TODO: Handle all forward references here?
// Handle #pragma constructor_for()
List < ProcedureRef > constructorProcs = new ArrayList < > ( ) ;
2020-08-25 23:24:04 +00:00
for ( KickCParser . PragmaContext pragmaConstructorFor : pragmaConstructorFors ) {
final List < KickCParser . PragmaParamContext > names = pragmaConstructorFor . pragmaParam ( ) ;
2020-08-23 22:35:48 +00:00
if ( names . size ( ) < 2 )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " #pragma constructor_for requires at least 2 parameters. " , new StatementSource ( pragmaConstructorFor ) ) ;
2020-08-25 23:24:04 +00:00
final String constructorProcName = pragmaParamName ( names . get ( 0 ) ) ;
2020-08-23 22:35:48 +00:00
final Procedure constructorProc = program . getScope ( ) . getLocalProcedure ( constructorProcName ) ;
if ( constructorProc = = null )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Constructor procedure not found " + constructorProcName , new StatementSource ( pragmaConstructorFor ) ) ;
2024-01-01 15:50:19 +00:00
if ( ! constructorProc . isAsmImportLibrary ( ) ) { // #820/41 - Only generate constructor_for when there is no import.
for ( int i = 1 ; i < names . size ( ) ; i + + ) {
final String procName = pragmaParamName ( names . get ( i ) ) ;
final Procedure proc = program . getScope ( ) . getLocalProcedure ( procName ) ;
if ( proc = = null )
throw new CompileError ( " Procedure not found " + procName , new StatementSource ( pragmaConstructorFor ) ) ;
if ( program . getLog ( ) . isVerboseParse ( ) )
program . getLog ( ) . append ( " Added constructor procedure " + constructorProc . getRef ( ) . toString ( ) + " to procedure " + proc . getRef ( ) . toString ( ) ) ;
proc . getConstructorRefs ( ) . add ( constructorProc . getRef ( ) ) ;
}
if ( ! constructorProcs . contains ( constructorProc . getRef ( ) ) ) {
constructorProcs . add ( constructorProc . getRef ( ) ) ;
// Add call to constructor procedure to the __init() procedure
addStatement ( new StatementCall ( null , constructorProc . getLocalName ( ) , new ArrayList < > ( ) , new StatementSource ( pragmaConstructorFor ) , Comment . NO_COMMENTS ) ) ;
// Mark the constructor procedure
constructorProc . setConstructor ( true ) ;
// #820/41 - Export the constructor_for procedure(s) so that when imported, they are not generated.
2024-04-26 09:40:14 +00:00
AsmExportLibrary asmExportLibrary = program . getAsmExportLibraries ( ) . get ( program . getAsmLibraryName ( ) ) ;
2024-01-02 18:41:03 +00:00
if ( asmExportLibrary ! = null ) {
program . addAsmExportProcedure ( asmExportLibrary , Procedure . CallingConvention . VAR_CALL , constructorProc . getLocalName ( ) ) ;
constructorProc . setAsmExportLibrary ( program . getAsmLibraryName ( ) ) ;
}
2024-01-01 15:50:19 +00:00
}
2020-08-23 22:35:48 +00:00
}
}
2020-06-18 08:01:45 +00:00
// Finalize the _init() procedure - if present
final ProcedureRef initProcedureRef = new ProcedureRef ( SymbolRef . INIT_PROC_NAME ) ;
final ProcedureCompilation initCompilation = program . getProcedureCompilation ( initProcedureRef ) ;
2020-06-20 19:31:49 +00:00
if ( initCompilation ! = null ) {
final StatementSequence initSequence = initCompilation . getStatementSequence ( ) ;
final Label initReturnLabel = program . getScope ( ) . getProcedure ( initProcedureRef ) . addLabel ( SymbolRef . PROCEXIT_BLOCK_NAME ) ;
2023-04-06 06:44:31 +00:00
initSequence . addStatement ( new StatementLabel ( initReturnLabel . getRef ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
initSequence . addStatement ( new StatementReturn ( null , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
initSequence . addStatement ( new StatementProcedureEnd ( initProcedureRef , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
2020-06-18 08:01:45 +00:00
}
// Add the _start() procedure to the program
2020-06-20 19:31:49 +00:00
{
2023-11-18 10:44:08 +00:00
String startProcedureName = SymbolRef . START_PROC_NAME ;
2024-01-01 15:50:19 +00:00
if ( program . getAsmLibraryName ( ) ! = null ) {
2023-12-12 07:20:06 +00:00
startProcedureName = " __ " + program . getAsmLibraryLabel ( ) + " _start " ;
2023-11-18 10:44:08 +00:00
}
program . setStartProcedure ( new ProcedureRef ( startProcedureName ) ) ;
final Procedure startProcedure = new Procedure ( program . getStartProcedureName ( ) , new SymbolTypeProcedure ( SymbolType . VOID , new ArrayList < > ( ) ) , program . getScope ( ) , Scope . SEGMENT_CODE_DEFAULT , Scope . SEGMENT_DATA_DEFAULT , Procedure . CallingConvention . PHI_CALL , Bank . COMMON ) ;
2020-06-20 19:31:49 +00:00
startProcedure . setParameters ( new ArrayList < > ( ) ) ;
program . getScope ( ) . add ( startProcedure ) ;
final ProcedureCompilation startProcedureCompilation = program . createProcedureCompilation ( startProcedure . getRef ( ) ) ;
2024-01-01 15:50:19 +00:00
if ( program . getAsmLibraryName ( ) ! = null ) {
2024-04-26 09:40:14 +00:00
AsmExportLibrary asmExportLibrary = program . getAsmExportLibraries ( ) . get ( program . getAsmLibraryName ( ) ) ;
2023-12-30 19:33:13 +00:00
program . addAsmExportProcedure ( asmExportLibrary , Procedure . CallingConvention . VAR_CALL , startProcedureName ) ;
2024-01-01 15:50:19 +00:00
startProcedure . setAsmExportLibrary ( program . getAsmLibraryName ( ) ) ;
2023-12-30 19:33:13 +00:00
}
2020-06-20 19:31:49 +00:00
final StatementSequence startSequence = startProcedureCompilation . getStatementSequence ( ) ;
2023-04-06 06:44:31 +00:00
startSequence . addStatement ( new StatementProcedureBegin ( startProcedure . getRef ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
2020-06-20 19:31:49 +00:00
if ( initCompilation ! = null )
2023-04-06 06:44:31 +00:00
startSequence . addStatement ( new StatementCall ( null , SymbolRef . INIT_PROC_NAME , new ArrayList < > ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
2021-08-05 20:33:46 +00:00
2023-04-22 12:58:33 +00:00
// Only check for a main function if the compilation is not for a library of .asm functions to be included during linkage.
// Libraries are activated using the #pragma library( library_name ) option.
2024-01-01 15:50:19 +00:00
if ( program . getAsmLibraryName ( ) = = null ) {
2023-12-12 07:20:06 +00:00
// 820/17 - For each imported library, call the start procedure of the library.
2024-04-26 09:40:14 +00:00
for ( AsmLibrary importedLibrary : program . getAsmImportLibraries ( ) . values ( ) ) {
2024-01-16 05:01:05 +00:00
String procedureStartName = " __ " + importedLibrary . getAsmLibraryIdentifier ( ) + " _start " ;
2023-12-12 07:20:06 +00:00
Procedure procedureStart = this . program . getScope ( ) . getProcedure ( new ProcedureRef ( procedureStartName ) ) ;
if ( procedureStart ! = null ) {
startSequence . addStatement ( new StatementCall ( null , procedureStartName , new ArrayList < > ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
}
}
2023-04-22 12:58:33 +00:00
final Procedure mainProc = program . getScope ( ) . getLocalProcedure ( SymbolRef . MAIN_PROC_NAME ) ;
if ( mainProc = = null )
throw new CompileError ( " Required main() not defined in program. " ) ;
if ( ! SymbolType . VOID . equals ( mainProc . getReturnType ( ) ) & & ! SymbolType . SWORD . equals ( mainProc . getReturnType ( ) ) )
throw new CompileError ( " return of main() must be 'void' or of type 'int'. " , mainProc . getDefinitionSource ( ) ) ;
if ( mainProc . getParameterNames ( ) . size ( ) = = 0 ) {
startSequence . addStatement ( new StatementCall ( null , SymbolRef . MAIN_PROC_NAME , new ArrayList < > ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
} else if ( mainProc . getParameterNames ( ) . size ( ) = = 2 ) {
final List < Variable > parameters = mainProc . getParameters ( ) ;
final Variable argc = parameters . get ( 0 ) ;
if ( ! SymbolType . SWORD . equals ( argc . getType ( ) ) )
throw new CompileError ( " first parameter of main() must be of type 'int'. " , mainProc . getDefinitionSource ( ) ) ;
final Variable argv = parameters . get ( 1 ) ;
if ( ! argv . getType ( ) . equals ( new SymbolTypePointer ( new SymbolTypePointer ( SymbolType . BYTE ) ) ) )
throw new CompileError ( " second parameter of main() must be of type 'char **'. " , mainProc . getDefinitionSource ( ) ) ;
final ArrayList < RValue > params = new ArrayList < > ( ) ;
params . add ( new ConstantInteger ( 0L , SymbolType . SWORD ) ) ;
params . add ( new ConstantPointer ( 0L , new SymbolTypePointer ( SymbolType . BYTE ) ) ) ;
startSequence . addStatement ( new StatementCall ( null , SymbolRef . MAIN_PROC_NAME , params , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
} else
throw new CompileError ( " main() has wrong number of parameters. It must have zero or 2 parameters. " , mainProc . getDefinitionSource ( ) ) ;
}
2021-08-05 20:33:46 +00:00
2023-11-18 10:44:08 +00:00
2020-06-20 19:31:49 +00:00
final Label startReturnLabel = startProcedure . addLabel ( SymbolRef . PROCEXIT_BLOCK_NAME ) ;
2023-04-06 06:44:31 +00:00
startSequence . addStatement ( new StatementLabel ( startReturnLabel . getRef ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
startSequence . addStatement ( new StatementReturn ( null , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
startSequence . addStatement ( new StatementProcedureEnd ( startProcedure . getRef ( ) , StatementSource . NONE , Comment . NO_COMMENTS ) ) ;
2020-06-20 19:31:49 +00:00
}
2020-06-18 08:01:45 +00:00
2017-05-08 16:08:07 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
2017-05-23 06:51:39 +00:00
public Void visitFile ( KickCParser . FileContext ctx ) {
2020-08-23 22:35:48 +00:00
if ( program . getMainFileComments ( ) = = null ) {
2019-02-17 10:03:55 +00:00
// Only set program file level comments for the first file.
2020-08-23 22:35:48 +00:00
program . setMainFileComments ( ensureUnusedComments ( getCommentsFile ( ctx ) ) ) ;
2019-02-17 10:03:55 +00:00
}
2017-12-01 22:45:09 +00:00
this . visit ( ctx . declSeq ( ) ) ;
return null ;
}
2020-06-15 20:23:06 +00:00
2020-08-23 22:35:48 +00:00
@Override
2020-08-25 23:24:04 +00:00
public Object visitPragma ( KickCParser . PragmaContext ctx ) {
final String pragmaName = ctx . NAME ( ) . getText ( ) ;
switch ( pragmaName ) {
2023-04-06 20:46:28 +00:00
case CParser . PRAGMA_TARGET - >
throw new InternalError ( " Error! #pragma target() should be handled in preprocessor! " ) ;
case CParser . PRAGMA_CPU - > {
2020-08-25 23:24:04 +00:00
final String cpuName = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
TargetCpu cpu = TargetCpu . getTargetCpu ( cpuName , false ) ;
program . getTargetPlatform ( ) . setCpu ( cpu ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_VAR_MODEL - > {
2020-08-25 23:24:04 +00:00
final List < KickCParser . PragmaParamContext > pragmaParams = ctx . pragmaParam ( ) ;
List < String > settings = pragmaParams . stream ( ) . map ( Pass0GenerateStatementSequence : : pragmaParamName ) . collect ( Collectors . toList ( ) ) ;
2021-07-25 00:21:25 +00:00
final VariableBuilderConfig config = VariableBuilderConfig . fromSettings ( settings , new StatementSource ( ctx ) ) ;
config . setStructModelClassic ( program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) . isStructModelClassic ( ) ) ;
program . getTargetPlatform ( ) . setVariableBuilderConfig ( config ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_STRUCT_MODEL - > {
2021-07-25 00:21:25 +00:00
final String modelName = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
if ( modelName . equalsIgnoreCase ( " classic " ) )
program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) . setStructModelClassic ( true ) ;
else if ( modelName . equalsIgnoreCase ( " unwind " ) )
program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) . setStructModelClassic ( true ) ;
else
throw new CompileError ( " Unknown struct model " + modelName , new StatementSource ( ctx ) ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_LINKSCRIPT - > {
2020-08-25 23:24:04 +00:00
final String linkScriptName = pragmaParamString ( pragmaParamSingle ( ctx ) ) ;
program . getLog ( ) . append ( " Loading link script \" " + linkScriptName + " \" " ) ;
Path currentPath = cParser . getSourceFolderPath ( ctx ) ;
final File linkScriptFile = SourceLoader . loadFile ( linkScriptName , currentPath , program . getTargetPlatformPaths ( ) ) ;
2023-12-16 12:45:38 +00:00
program . getTargetPlatform ( ) . setLinkScriptFile ( linkScriptFile , false ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_EXTENSION - > {
2020-08-25 23:24:04 +00:00
String extension = pragmaParamString ( pragmaParamSingle ( ctx ) ) ;
program . getTargetPlatform ( ) . setOutFileExtension ( extension ) ;
2021-05-15 09:17:17 +00:00
program . getOutputFileManager ( ) . setBinaryExtension ( extension ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_EMULATOR - > {
2020-08-25 23:24:04 +00:00
String emuName = pragmaParamString ( pragmaParamSingle ( ctx ) ) ;
program . getTargetPlatform ( ) . setEmulatorCommand ( emuName ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_ENCODING - > {
2020-08-25 23:24:04 +00:00
final String encodingName = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
try {
this . currentEncoding = StringEncoding . fromName ( encodingName . toUpperCase ( Locale . ENGLISH ) ) ;
} catch ( IllegalArgumentException e ) {
throw new CompileError ( " Unknown string encoding " + encodingName , new StatementSource ( ctx ) ) ;
}
2023-05-21 17:16:51 +00:00
}
case CParser . PRAGMA_CODE_SEG - > this . currentSegmentCode = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
case CParser . PRAGMA_DATA_SEG - > this . currentSegmentData = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
case CParser . PRAGMA_BANK - > {
2023-04-23 09:54:47 +00:00
if ( ctx . pragmaParam ( ) . size ( ) ! = 2 )
throw new CompileError ( " #pragma expects two parameters! " , new StatementSource ( ctx ) ) ;
2022-11-19 06:45:07 +00:00
try {
2023-04-11 11:41:14 +00:00
final String pragmaBankArea = pragmaParamName ( ctx . pragmaParam ( 0 ) ) ;
final Number pragmaBank = pragmaParamNumber ( ctx . pragmaParam ( 1 ) ) ;
this . currentBank = new Bank ( pragmaBankArea , pragmaBank . longValue ( ) ) ;
2022-11-19 06:45:07 +00:00
} catch ( IllegalArgumentException e ) {
2023-04-23 09:54:47 +00:00
throw new CompileError ( " Illegal bank parameter " + ctx . getText ( ) , new StatementSource ( ctx ) ) ;
2022-11-19 06:45:07 +00:00
}
2023-05-21 17:16:51 +00:00
}
case CParser . PRAGMA_NOBANK - > this . currentBank = Bank . COMMON ; // When the current segment is null, the procedure will not be declared as far.
2023-04-06 20:46:28 +00:00
case CParser . PRAGMA_RESOURCE - > {
2021-09-25 20:04:47 +00:00
String resourceFileName = pragmaParamString ( pragmaParamSingle ( ctx ) ) ;
addResourceFile ( ctx , resourceFileName ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_START_ADDRESS - > {
2020-11-15 01:15:48 +00:00
Number startAddress = pragmaParamNumber ( pragmaParamSingle ( ctx ) ) ;
program . getTargetPlatform ( ) . setStartAddress ( startAddress ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_CALLING - > currentCallingConvention = pragmaParamCallingConvention ( pragmaParamSingle ( ctx ) ) ;
case CParser . PRAGMA_INTERRUPT - > this . currentInterruptType = pragmaParamName ( pragmaParamSingle ( ctx ) ) ;
case CParser . PRAGMA_ZP_RESERVE - > {
2020-08-25 23:24:04 +00:00
List < Integer > reservedZps = pragmaParamRanges ( ctx . pragmaParam ( ) ) ;
program . addReservedZps ( reservedZps ) ;
2023-04-06 20:46:28 +00:00
}
case CParser . PRAGMA_CONSTRUCTOR_FOR - > {
2023-10-27 13:31:51 +00:00
this . pragmaConstructorFors . add ( ctx ) ;
return null ;
2023-04-22 12:58:33 +00:00
}
2023-11-05 15:51:54 +00:00
case CParser . PRAGMA_ASM_LIBRARY - > { // Defines that the result is an asm routine or capability library instead of a program.
2024-01-16 05:01:05 +00:00
String asmLibraryName = null ;
2023-12-12 07:20:06 +00:00
boolean exportAll = true ;
if ( ctx . pragmaParam ( ) . size ( ) = = 0 ) {
StatementSource stmtSource = new StatementSource ( ctx ) ;
Path filePath = Paths . get ( stmtSource . getFileName ( ) ) ;
2024-01-16 05:01:05 +00:00
String mainBase = program . getOutputFileManager ( ) . getOutputBaseName ( ) ;
String stmtBase = filePath . getFileName ( ) . toString ( ) ;
stmtBase = FileNameUtils . removeExtension ( stmtBase ) ;
if ( mainBase . equals ( stmtBase ) ) {
asmLibraryName = stmtBase ;
}
2023-12-12 07:20:06 +00:00
} else if ( ctx . pragmaParam ( ) . size ( ) = = 1 ) {
asmLibraryName = pragmaParamString ( pragmaParamSingle ( ctx ) ) ;
exportAll = false ;
} else {
throw new CompileError ( " #pragma asm_library: too many parameters! " , new StatementSource ( ctx ) ) ;
}
2024-01-16 05:01:05 +00:00
if ( asmLibraryName ! = null ) {
if ( ! program . hasAsmImportLibrary ( asmLibraryName ) ) {
program . setAsmLibraryName ( asmLibraryName ) ;
program . addAsmExportLibrary ( asmLibraryName , exportAll ) ;
2024-04-19 13:55:24 +00:00
// The scope of the program needs to have a reference to the name of the .asm library.
// For use in AsmFormat!
program . getScope ( ) . setAsmLibraryName ( asmLibraryName ) ;
2024-01-16 05:01:05 +00:00
}
2023-12-12 07:20:06 +00:00
}
2023-04-06 20:46:28 +00:00
}
2023-11-18 09:40:50 +00:00
case CParser . PRAGMA_ASM_EXPORT - > { // Defines that an C routine is exported into the asm_library.
2024-01-16 05:01:05 +00:00
String asmExportLibraryName = program . getAsmLibraryName ( ) ;
if ( asmExportLibraryName ! = null ) {
// Only export the procedures when there is a library declared in the main program!
// Otherwise ignore the asm_export #pragma.
Procedure . CallingConvention callingConvention = currentCallingConvention ;
List < String > procedures = pragmaParamAsmExportProcedures ( ctx . pragmaParam ( ) ) ;
AsmExportLibrary asmExportLibrary = program . addAsmExportLibrary ( asmExportLibraryName , false ) ;
2024-04-19 13:55:24 +00:00
program . addAsmExportProcedures ( asmExportLibrary , currentCallingConvention , procedures ) ;
// TODO: #820/2 - Ensure that an export can be done with a calling convention tagged.
// Versus the current currentCallingConvention implementation.
//program.addAsmExportProcedures(asmExportLibrary, Procedure.CallingConvention.VAR_CALL, procedures);
2024-01-16 05:01:05 +00:00
}
2023-11-18 09:40:50 +00:00
}
2023-11-05 15:51:54 +00:00
case CParser . PRAGMA_ASM_IMPORT - > { // Defines that an asm routine or capability library is imported into the program.
String libraryName = pragmaParamString ( pragmaParamFirst ( ctx ) ) ;
2023-11-18 09:40:50 +00:00
Procedure . CallingConvention callingConvention = pragmaParamCallingConvention ( ctx . pragmaParam ( 1 ) ) ;
List < String > procedures = pragmaParamAsmImportProcedures ( ctx . pragmaParam ( ) ) ;
2023-11-05 15:51:54 +00:00
2024-01-01 15:50:19 +00:00
AsmImportLibrary asmImportLibrary = program . addAsmImportLibrary ( libraryName ) ;
program . addAsmImportProcedures ( asmImportLibrary , callingConvention , procedures ) ;
2023-11-05 15:51:54 +00:00
}
2023-10-27 13:31:51 +00:00
default - > program . getLog ( ) . append ( " Warning! Unknown #pragma " + pragmaName ) ;
2020-08-25 23:24:04 +00:00
}
2020-08-23 22:35:48 +00:00
return null ;
}
2023-11-05 15:51:54 +00:00
/ * *
* Check that a # pragma has a parameter - and return that parameter
*
* @param ctx The # pragma
* @return The single parameter
* /
private static KickCParser . PragmaParamContext pragmaParamFirst ( KickCParser . PragmaContext ctx ) {
if ( ctx . pragmaParam ( ) . size ( ) < 1 )
throw new CompileError ( " #pragma expects minimum one parameter! " , new StatementSource ( ctx ) ) ;
return ctx . pragmaParam ( ) . get ( 0 ) ;
}
2020-08-25 23:24:04 +00:00
/ * *
* Check that a # pragma has a single parameter - and return that parameter
*
* @param ctx The # pragma
* @return The single parameter
* /
private static KickCParser . PragmaParamContext pragmaParamSingle ( KickCParser . PragmaContext ctx ) {
if ( ctx . pragmaParam ( ) . size ( ) ! = 1 )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " #pragma expects a single parameter! " , new StatementSource ( ctx ) ) ;
2020-08-25 23:24:04 +00:00
return ctx . pragmaParam ( ) . get ( 0 ) ;
2020-02-08 09:35:43 +00:00
}
2020-08-25 23:24:04 +00:00
/ * *
* Parse a single NUMBER parameter of a # pragma
* If the parameter is not a NUMBER the compiler will fail out
*
* @param paramCtx The parameter to parse
* @return The number
* /
private static Number pragmaParamNumber ( KickCParser . PragmaParamContext paramCtx ) {
if ( ! ( paramCtx instanceof KickCParser . PragmaParamNumberContext ) )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a NUMBER parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
final Number number = NumberParser . parseLiteral ( ( ( KickCParser . PragmaParamNumberContext ) paramCtx ) . NUMBER ( ) . getText ( ) ) ;
if ( number = = null )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a NUMBER parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
return number ;
2019-08-09 15:07:11 +00:00
}
2020-08-25 23:24:04 +00:00
/ * *
* Parse a single CALLINGCONVENTION parameter of a # pragma
* If the parameter is not a CALLINGCONVENTION the compiler will fail out
*
* @param paramCtx The parameter to parse
* @return The name
* /
private static Procedure . CallingConvention pragmaParamCallingConvention ( KickCParser . PragmaParamContext paramCtx ) {
if ( ! ( paramCtx instanceof KickCParser . PragmaParamCallingConventionContext ) )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a CALLINGCONVENTION parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
final String callingConventionName = ( ( KickCParser . PragmaParamCallingConventionContext ) paramCtx ) . CALLINGCONVENTION ( ) . getText ( ) ;
2023-11-24 14:46:48 +00:00
final Procedure . CallingConvention callingConvention = Procedure . CallingConvention . getCallingConvention ( callingConventionName ) ;
2020-08-25 23:24:04 +00:00
if ( callingConvention = = null )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a CALLINGCONVENTION parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
return callingConvention ;
2020-05-17 21:33:18 +00:00
}
2020-08-25 23:24:04 +00:00
/ * *
* Parse a single NAME parameter of a # pragma
* If the parameter is not a NAME the compiler will fail out
*
* @param paramCtx The parameter to parse
* @return The name
* /
private static String pragmaParamName ( KickCParser . PragmaParamContext paramCtx ) {
if ( ! ( paramCtx instanceof KickCParser . PragmaParamNameContext ) )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a NAME parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
return ( ( KickCParser . PragmaParamNameContext ) paramCtx ) . NAME ( ) . getText ( ) ;
2020-05-09 06:12:28 +00:00
}
2020-08-25 23:24:04 +00:00
/ * *
* Parse a single STRING parameter of a # pragma
* If the parameter is not a STRING the compiler will fail out
*
* @param paramCtx The parameter to parse
* @return The string
* /
private static String pragmaParamString ( KickCParser . PragmaParamContext paramCtx ) {
if ( ! ( paramCtx instanceof KickCParser . PragmaParamStringContext ) )
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a STRING parameter. Found ' " + paramCtx . getText ( ) + " '. " , new StatementSource ( paramCtx . getParent ( ) ) ) ;
2020-08-25 23:24:04 +00:00
final String stringLiteral = ( ( KickCParser . PragmaParamStringContext ) paramCtx ) . STRING ( ) . getText ( ) ;
return stringLiteral . substring ( 1 , stringLiteral . length ( ) - 1 ) ;
2019-04-19 23:44:54 +00:00
}
2020-05-22 13:24:03 +00:00
/ * *
2020-08-25 23:24:04 +00:00
* Find a reserved ZP - addresses from a list of pragma parameters ( consisting of numbers and number ranges ) .
2020-06-15 20:23:06 +00:00
*
2020-05-22 13:24:03 +00:00
* @param reserveParams The params
* @return The list of reserved zeropage addresses
* /
2020-08-25 23:24:04 +00:00
private List < Integer > pragmaParamRanges ( List < KickCParser . PragmaParamContext > reserveParams ) {
2020-05-22 13:24:03 +00:00
List < Integer > reservedZps = new ArrayList < > ( ) ;
2020-08-25 23:24:04 +00:00
for ( KickCParser . PragmaParamContext reserveCtx : reserveParams ) {
if ( reserveCtx instanceof KickCParser . PragmaParamNumberContext ) {
final TerminalNode number = ( ( KickCParser . PragmaParamNumberContext ) reserveCtx ) . NUMBER ( ) ;
2020-05-22 13:24:03 +00:00
// Only a single reserved address
2020-08-25 23:24:04 +00:00
Number reservedZp = NumberParser . parseLiteral ( number . getText ( ) ) ;
2020-05-22 13:24:03 +00:00
reservedZps . add ( reservedZp . intValue ( ) ) ;
2020-08-25 23:24:04 +00:00
} else if ( reserveCtx instanceof KickCParser . PragmaParamRangeContext ) {
final TerminalNode rangeStart = ( ( KickCParser . PragmaParamRangeContext ) reserveCtx ) . NUMBER ( 0 ) ;
final TerminalNode rangeEnd = ( ( KickCParser . PragmaParamRangeContext ) reserveCtx ) . NUMBER ( 1 ) ;
2020-05-22 13:24:03 +00:00
// A range of reserved addresses
Number startZp = NumberParser . parseLiteral ( rangeStart . getText ( ) ) ;
Number endZp = NumberParser . parseLiteral ( rangeEnd . getText ( ) ) ;
int zp = startZp . intValue ( ) ;
2020-06-15 20:23:06 +00:00
while ( zp < = endZp . intValue ( ) ) {
2020-05-22 13:24:03 +00:00
reservedZps . add ( zp ) ;
zp + + ;
}
2020-08-25 23:24:04 +00:00
} else {
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Expected a NUMBER or RANGE parameter. Found ' " + reserveCtx . getText ( ) + " '. " , new StatementSource ( reserveCtx . getParent ( ) ) ) ;
2020-05-22 13:24:03 +00:00
}
}
return reservedZps ;
}
2023-11-05 15:51:54 +00:00
/ * *
* Parse all the function names from the library import function list , delimited by comma .
*
* @param libraryFunctionParams The library function params
* @return The list of parsed function parameters
* /
2023-11-18 09:40:50 +00:00
private List < String > pragmaParamAsmImportProcedures ( List < KickCParser . PragmaParamContext > libraryParams ) {
2023-11-05 15:51:54 +00:00
List < String > procedures = new ArrayList < > ( ) ;
2023-11-18 09:40:50 +00:00
for ( KickCParser . PragmaParamContext reserveCtx : libraryParams . subList ( 2 , libraryParams . size ( ) ) ) {
if ( reserveCtx instanceof KickCParser . PragmaParamNameContext ) {
final TerminalNode name = ( ( KickCParser . PragmaParamNameContext ) reserveCtx ) . NAME ( ) ;
// Only a single reserved address
String procedureName = name . getText ( ) ;
procedures . add ( procedureName ) ;
} else {
throw new CompileError ( " Expected a NAME parameter. Found ' " + reserveCtx . getText ( ) + " '. " , new StatementSource ( reserveCtx . getParent ( ) ) ) ;
}
}
return procedures ;
}
/ * *
* Parse all the function names from the library export function list , delimited by comma .
*
* @param libraryParams The library function params
* @return The list of parsed function parameters
* /
private List < String > pragmaParamAsmExportProcedures ( List < KickCParser . PragmaParamContext > libraryParams ) {
List < String > procedures = new ArrayList < > ( ) ;
2023-12-15 13:51:18 +00:00
for ( KickCParser . PragmaParamContext reserveCtx : libraryParams . subList ( 0 , libraryParams . size ( ) ) ) {
2023-11-05 15:51:54 +00:00
if ( reserveCtx instanceof KickCParser . PragmaParamNameContext ) {
final TerminalNode name = ( ( KickCParser . PragmaParamNameContext ) reserveCtx ) . NAME ( ) ;
// Only a single reserved address
String procedureName = name . getText ( ) ;
procedures . add ( procedureName ) ;
} else {
throw new CompileError ( " Expected a NAME parameter. Found ' " + reserveCtx . getText ( ) + " '. " , new StatementSource ( reserveCtx . getParent ( ) ) ) ;
}
}
return procedures ;
}
2019-09-16 23:11:51 +00:00
/** The current calling convention for procedures. */
2020-02-27 06:22:22 +00:00
private Procedure . CallingConvention currentCallingConvention ;
2019-09-16 23:11:51 +00:00
2020-12-21 00:04:34 +00:00
/** The current code segment. */
2023-04-11 08:13:41 +00:00
private String currentSegmentCode = Scope . SEGMENT_CODE_DEFAULT ;
2019-08-09 20:38:33 +00:00
2020-12-21 00:04:34 +00:00
/** The current data segment. */
2023-04-11 08:13:41 +00:00
private String currentSegmentData = Scope . SEGMENT_DATA_DEFAULT ;
2019-08-09 20:38:33 +00:00
2023-04-11 08:13:41 +00:00
/** The current far segment. If null, the sequent procedures won't be banked. */
2023-04-23 20:04:23 +00:00
private Bank currentBank = Bank . COMMON ;
2019-08-09 20:38:33 +00:00
2020-12-21 00:04:34 +00:00
/** The current default interrupt type. */
private String currentInterruptType ;
2017-12-01 22:45:09 +00:00
@Override
2018-04-29 19:39:12 +00:00
public Object visitDeclFunction ( KickCParser . DeclFunctionContext ctx ) {
2020-03-27 21:23:04 +00:00
this . visit ( ctx . declType ( ) ) ;
2021-05-02 22:50:10 +00:00
this . visit ( ctx . declarator ( ) ) ;
2021-05-11 21:11:51 +00:00
2023-11-05 15:51:54 +00:00
String procedureName = varDecl . getVarName ( ) ;
Procedure procDeclared = ( Procedure ) program . getScope ( ) . getSymbol ( new SymbolRef ( procedureName ) ) ;
2024-01-01 15:50:19 +00:00
AsmImportLibrary asmImport = this . program . getProcedureAsmImportLibrary ( procedureName ) ;
2021-05-11 21:11:51 +00:00
2023-11-05 15:51:54 +00:00
// We skip the procedure definition if:
// - it is already defined in an asm library.
// - it is declared as extern.
if ( asmImport = = null & & ( procDeclared = = null | | ! procDeclared . isDeclaredExtern ( ) ) ) {
2020-04-08 21:40:27 +00:00
2023-11-05 15:51:54 +00:00
// Declare & define the procedure
Procedure procedure = declareProcedure ( true , ctx , StatementSource . procedureDecl ( ctx ) ) ;
2021-05-11 21:11:51 +00:00
2023-11-05 15:51:54 +00:00
varDecl . exitType ( ) ;
2021-05-11 21:11:51 +00:00
2023-11-05 15:51:54 +00:00
// enter the procedure
scopeStack . push ( procedure ) ;
2021-05-16 08:19:21 +00:00
2023-11-05 15:51:54 +00:00
/** Copied to solve Issue #820 - the preparation of the return block */
Variable returnVar = procedure . getLocalVar ( " return " ) ;
// Add the body
addStatement ( new StatementProcedureBegin ( procedure . getRef ( ) , StatementSource . procedureBegin ( ctx ) , Comment . NO_COMMENTS ) ) ;
2021-05-12 06:58:10 +00:00
2023-11-05 15:51:54 +00:00
Label procExit = procedure . addLabel ( SymbolRef . PROCEXIT_BLOCK_NAME ) ;
if ( ctx . stmtSeq ( ) ! = null ) {
this . visit ( ctx . stmtSeq ( ) ) ;
}
addStatement ( new StatementLabel ( procExit . getRef ( ) , StatementSource . procedureEnd ( ctx ) , Comment . NO_COMMENTS ) ) ;
if ( Procedure . CallingConvention . PHI_CALL . equals ( procedure . getCallingConvention ( ) ) & & returnVar ! = null & & returnVar . isKindPhiMaster ( ) ) {
addStatement ( new StatementAssignment ( returnVar . getVariableRef ( ) , returnVar . getRef ( ) , false , StatementSource . procedureEnd ( ctx ) , Comment . NO_COMMENTS ) ) ;
}
SymbolVariableRef returnVarRef = null ;
if ( returnVar ! = null ) {
returnVarRef = returnVar . getRef ( ) ;
}
addStatement ( new StatementReturn ( returnVarRef , StatementSource . procedureEnd ( ctx ) , Comment . NO_COMMENTS ) ) ;
addStatement ( new StatementProcedureEnd ( procedure . getRef ( ) , StatementSource . procedureEnd ( ctx ) , Comment . NO_COMMENTS ) ) ;
// exit the procedure
scopeStack . pop ( ) ;
}
2020-04-08 21:40:27 +00:00
2021-05-11 21:11:51 +00:00
return null ;
}
2021-05-12 06:58:10 +00:00
/ * *
* Declare a procedure ( either as part of a forward declaration or as part of a definition . )
2021-05-11 21:11:51 +00:00
* Finds the name , type and parameters in the varDecl .
* If the procedure is already declared then it is checked that the current declaration matches the existing one - and the existing one is returned .
*
2021-05-12 06:58:10 +00:00
* @param defineProcedure If true the procedure parameter and return variables will also be defined
2021-05-11 21:11:51 +00:00
* @param ctx The parser context ( used to find any comments . )
* @param statementSource The statements source ( used when producing errors .
* @return The declared procedure .
* /
2021-05-12 06:58:10 +00:00
private Procedure declareProcedure ( boolean defineProcedure , ParserRuleContext ctx , StatementSource statementSource ) {
2023-04-11 08:13:41 +00:00
Procedure procedure = new Procedure ( varDecl . getVarName ( ) , ( SymbolTypeProcedure ) varDecl . getEffectiveType ( ) , program . getScope ( ) , currentSegmentCode , currentSegmentData , currentCallingConvention , currentBank ) ;
2023-05-21 17:16:51 +00:00
addDirectives ( procedure , varDecl . getDeclDirectives ( ) ) ;
2021-05-11 21:11:51 +00:00
// Check if the declaration matches any existing declaration!
2020-04-08 21:40:27 +00:00
final Symbol existingSymbol = program . getScope ( ) . getSymbol ( procedure . getRef ( ) ) ;
2020-04-19 21:29:37 +00:00
if ( existingSymbol ! = null ) {
2020-04-08 21:40:27 +00:00
// Already declared - check equality
2020-04-18 18:54:39 +00:00
if ( ! ( existingSymbol instanceof Procedure ) | | ! SymbolTypeConversion . procedureDeclarationMatch ( ( Procedure ) existingSymbol , procedure ) )
2021-05-12 06:58:10 +00:00
throw new CompileError ( " Conflicting declarations for procedure: " + procedure . getFullName ( ) , statementSource ) ;
if ( defineProcedure ) {
// Check that the procedure is not already defined
Procedure existingProcedure = ( Procedure ) existingSymbol ;
if ( existingProcedure . isDeclaredIntrinsic ( ) )
throw new CompileError ( " Redefinition of procedure: " + procedure . getFullName ( ) , statementSource ) ;
final StatementSequence statementSequence = program . getProcedureCompilation ( existingProcedure . getRef ( ) ) . getStatementSequence ( ) ;
if ( statementSequence ! = null & & statementSequence . getStatements ( ) . size ( ) > 0 )
throw new CompileError ( " Redefinition of procedure " + procedure . getFullName ( ) , statementSource ) ;
}
2021-05-11 21:11:51 +00:00
procedure = ( Procedure ) existingSymbol ;
2020-04-08 21:40:27 +00:00
} else {
2024-01-01 15:50:19 +00:00
AsmImportLibrary asmImportLibrary = this . program . getProcedureAsmImportLibrary ( procedure . getFullName ( ) ) ;
if ( asmImportLibrary ! = null )
this . program . setProcedureAsAsmImport ( procedure , asmImportLibrary ) ;
2020-04-08 21:40:27 +00:00
program . getScope ( ) . add ( procedure ) ;
2020-06-15 20:23:06 +00:00
program . createProcedureCompilation ( procedure . getRef ( ) ) ;
2017-12-01 22:45:09 +00:00
}
2021-05-12 06:58:10 +00:00
2024-01-16 05:01:05 +00:00
/ * *
* If the procedure was define before the actual Library Import was used ,
* then the defined procedure must be removed , and only the declaration must be kept .
* /
2023-11-18 09:40:50 +00:00
// if(procedure.isDeclaredExtern() && !defineProcedure) {
if ( procedure . isDeclaredExtern ( ) & & ! defineProcedure & & existingSymbol = = null ) {
2023-10-27 13:31:51 +00:00
/ * * This is an almost exact copy of a procedure definition .
* When a procedure is defined external , the required control blocks are to be allocated
* for the procedure parameters and return value for the sequent steps in the compiler to work .
* However , during assembler generation , an externally defined procedure is completely ignored from being generated .
* In this way , without too much impact on the compiler design , external functions could be implemented .
*
* The code is copied and reworked from ( Search for # 820 ) :
* - Issue # 820 TAG A - the preparation of the parameters and the entry block .
* - Issue # 820 TAG B - the preparation of the return block .
*
* /
// Make sure comments, directives and source are from the definition
addDirectives ( procedure , varDecl . getDeclDirectives ( ) ) ;
procedure . setComments ( ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
procedure . setDefinitionSource ( statementSource ) ;
// enter the procedure
scopeStack . push ( procedure ) ;
// Add parameter variables...
boolean variableLengthParameterList = false ;
List < Variable > parameterList = new ArrayList < > ( ) ;
for ( ParameterDecl parameter : varDecl . parameters ) {
// Handle variable length parameter lists
if ( SymbolType . PARAM_LIST . equals ( parameter . type ) ) {
procedure . setVariableLengthParameterList ( true ) ;
variableLengthParameterList = true ;
continue ;
} else if ( variableLengthParameterList )
throw new CompileError ( " Variable length parameter list is only legal as the last parameter. " , statementSource ) ;
// Handle stray void parameters (Any single void parameter was removed by the type parser)
if ( SymbolType . VOID . equals ( parameter . type ) )
throw new CompileError ( " Illegal void parameter. " , statementSource ) ;
// Handle parameters without a name in the declaration
if ( parameter . name = = null )
throw new CompileError ( " Illegal unnamed parameter. " , statementSource ) ;
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( parameter . name , getCurrentScope ( ) , true , false , parameter . type , parameter . directives , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2023-10-27 13:31:51 +00:00
final Variable paramVar = varBuilder . build ( ) ;
parameterList . add ( paramVar ) ;
}
procedure . setParameters ( parameterList ) ;
procedure . setSegmentData ( currentSegmentData ) ; // When a procedure is defined, the currentDataSegment is to be set.
procedure . setSegmentCode ( currentSegmentCode ) ; // When a procedure is defined, the currentSegmentCode is to be set.
if ( procedure . getBank ( ) = = null & & currentBank ! = null ) {
procedure . setBank ( currentBank ) ; // When a procedure is defined, the currentBank is to be set, or far calls won't work.
}
// Add return variable
if ( ! SymbolType . VOID . equals ( procedure . getReturnType ( ) ) ) {
2024-04-26 09:40:14 +00:00
final VariableBuilder builder = new VariableBuilder ( " return " , procedure , false , false , procedure . getReturnType ( ) , varDecl . getDeclDirectives ( ) , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2023-10-27 13:31:51 +00:00
builder . build ( ) ;
}
Variable returnVar = procedure . getLocalVar ( " return " ) ;
// Add the body
addStatement ( new StatementProcedureBegin ( procedure . getRef ( ) , statementSource , Comment . NO_COMMENTS ) ) ;
Label procExit = procedure . addLabel ( SymbolRef . PROCEXIT_BLOCK_NAME ) ;
// Variable tmpVar = addIntermediateVar();
// RValue rValue = tmpVar.getRef();
// returnVar = procedure.getLocalVariable("return");
//
// addStatement(new StatementAssignment((LValue) returnVar.getRef(), rValue, false, statementSource, null));
// Label returnLabel = procedure.getLocalLabel(SymbolRef.PROCEXIT_BLOCK_NAME);
// addStatement(new StatementJump(returnLabel.getRef(), new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx))));
addStatement ( new StatementLabel ( procExit . getRef ( ) , statementSource , Comment . NO_COMMENTS ) ) ;
if ( Procedure . CallingConvention . PHI_CALL . equals ( procedure . getCallingConvention ( ) ) & & returnVar ! = null & & returnVar . isKindPhiMaster ( ) ) {
addStatement ( new StatementAssignment ( returnVar . getVariableRef ( ) , returnVar . getRef ( ) , false , statementSource , Comment . NO_COMMENTS ) ) ;
}
SymbolVariableRef returnVarRef = null ;
if ( returnVar ! = null ) {
returnVarRef = returnVar . getRef ( ) ;
returnVar . setDeclarationOnly ( false ) ; // The procedure is defined as extern and this property is interited by the variable.
// program.getScope().add(tmpVar);
//tmpVar.setDeclarationOnly(false); // Same for the temporary return value.
}
addStatement ( new StatementReturn ( returnVarRef , statementSource , Comment . NO_COMMENTS ) ) ;
addStatement ( new StatementProcedureEnd ( procedure . getRef ( ) , statementSource , Comment . NO_COMMENTS ) ) ;
scopeStack . pop ( ) ;
}
/** Copied to solve Issue #820 TAG A */
if ( defineProcedure & & ! procedure . isDeclaredExtern ( ) ) {
2021-05-16 08:19:21 +00:00
// Make sure comments, directives and source are from the definition
2023-04-06 20:46:28 +00:00
addDirectives ( procedure , varDecl . getDeclDirectives ( ) ) ;
2021-05-12 06:58:10 +00:00
procedure . setComments ( ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2021-05-16 08:19:21 +00:00
procedure . setDefinitionSource ( statementSource ) ;
2023-12-12 07:20:06 +00:00
// Export the procedure to an .asm export library when either the source is flagged as a library
// and/or specific procedures are flagged as a library.
// TODO: remove the library dependency and rework to one routine call.
2024-04-26 09:40:14 +00:00
AsmExportLibrary asmExport = this . program . getAsmExportLibraryFromSymbol ( procedure ) ;
2023-12-12 07:20:06 +00:00
if ( asmExport ! = null ) {
2023-12-15 13:51:18 +00:00
if ( ! program . isProcedureAsmExport ( procedure . getFullName ( ) ) & & asmExport . isExportAll ( ) ) {
2023-12-12 07:20:06 +00:00
program . addAsmExportProcedure ( asmExport , currentCallingConvention , procedure . getFullName ( ) ) ;
}
this . program . setProcedureAsAsmExport ( procedure , asmExport ) ;
}
2021-05-12 06:58:10 +00:00
// enter the procedure
scopeStack . push ( procedure ) ;
// Add parameter variables...
boolean variableLengthParameterList = false ;
List < Variable > parameterList = new ArrayList < > ( ) ;
for ( ParameterDecl parameter : varDecl . parameters ) {
// Handle variable length parameter lists
if ( SymbolType . PARAM_LIST . equals ( parameter . type ) ) {
procedure . setVariableLengthParameterList ( true ) ;
variableLengthParameterList = true ;
continue ;
} else if ( variableLengthParameterList )
throw new CompileError ( " Variable length parameter list is only legal as the last parameter. " , statementSource ) ;
// Handle stray void parameters (Any single void parameter was removed by the type parser)
if ( SymbolType . VOID . equals ( parameter . type ) )
throw new CompileError ( " Illegal void parameter. " , statementSource ) ;
2021-05-13 08:26:33 +00:00
// Handle parameters without a name in the declaration
2021-08-08 11:47:48 +00:00
if ( parameter . name = = null )
2021-05-13 08:26:33 +00:00
throw new CompileError ( " Illegal unnamed parameter. " , statementSource ) ;
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( parameter . name , getCurrentScope ( ) , true , false , parameter . type , null , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2021-05-12 06:58:10 +00:00
final Variable paramVar = varBuilder . build ( ) ;
parameterList . add ( paramVar ) ;
}
procedure . setParameters ( parameterList ) ;
2023-04-23 20:04:23 +00:00
procedure . setSegmentData ( currentSegmentData ) ;
procedure . setSegmentCode ( currentSegmentCode ) ;
if ( procedure . getBank ( ) . isCommon ( ) ) {
procedure . setBank ( currentBank ) ;
2023-04-11 06:35:53 +00:00
}
2021-05-12 06:58:10 +00:00
// Add return variable
if ( ! SymbolType . VOID . equals ( procedure . getReturnType ( ) ) ) {
2024-04-26 09:40:14 +00:00
final VariableBuilder builder = new VariableBuilder ( " return " , procedure , false , false , procedure . getReturnType ( ) , varDecl . getDeclDirectives ( ) , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2021-05-12 06:58:10 +00:00
builder . build ( ) ;
}
// exit the procedure
scopeStack . pop ( ) ;
}
2021-05-11 21:11:51 +00:00
return procedure ;
2017-12-01 22:45:09 +00:00
}
2021-05-12 06:58:10 +00:00
2017-12-01 22:45:09 +00:00
@Override
2021-05-13 08:26:33 +00:00
public Object visitParameterDeclTypeDeclarator ( KickCParser . ParameterDeclTypeDeclaratorContext ctx ) {
2020-03-27 21:23:04 +00:00
this . visit ( ctx . declType ( ) ) ;
2021-05-02 22:24:37 +00:00
this . visit ( ctx . declarator ( ) ) ;
2023-11-18 09:40:50 +00:00
ParameterDecl paramDecl = new ParameterDecl ( varDecl . getVarName ( ) , varDecl . getEffectiveType ( ) , varDecl . getDeclDirectives ( ) ) ;
2020-03-27 09:53:42 +00:00
varDecl . exitType ( ) ;
2021-05-11 21:11:51 +00:00
return paramDecl ;
2017-12-01 22:45:09 +00:00
}
2019-04-08 21:27:05 +00:00
@Override
2021-05-13 08:26:33 +00:00
public Object visitParameterDeclTypeName ( KickCParser . ParameterDeclTypeNameContext ctx ) {
SymbolType paramType = ( SymbolType ) this . visit ( ctx . typeName ( ) ) ;
2023-11-18 09:40:50 +00:00
return new ParameterDecl ( null , paramType , null ) ;
2019-04-08 21:27:05 +00:00
}
2020-04-19 21:29:37 +00:00
@Override
public Object visitParameterDeclList ( KickCParser . ParameterDeclListContext ctx ) {
2023-11-18 09:40:50 +00:00
return new ParameterDecl ( null , SymbolType . PARAM_LIST , null ) ;
2020-04-19 21:29:37 +00:00
}
2018-07-07 11:55:15 +00:00
@Override
2020-06-22 22:22:56 +00:00
public Object visitStmtDeclKasm ( KickCParser . StmtDeclKasmContext ctx ) {
2020-06-27 22:16:25 +00:00
final KickAsm kickAsm = ( KickAsm ) this . visit ( ctx . kasmContent ( ) ) ;
StatementKickAsm statementKickAsm = new StatementKickAsm ( kickAsm . kickAsmCode , kickAsm . bytes , kickAsm . cycles , kickAsm . uses , kickAsm . declaredClobber , StatementSource . kickAsm ( ctx . kasmContent ( ) ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2020-06-22 22:22:56 +00:00
addStatement ( statementKickAsm ) ;
return statementKickAsm ;
}
2020-06-27 10:21:29 +00:00
/** Inline KickAssembler (can be either inline code or inline data initialization). */
2020-06-22 22:22:56 +00:00
static class KickAsm {
/** KickAssembler code. */
private final String kickAsmCode ;
/** Variables/constants used by the kickasm code. */
private final List < SymbolRef > uses ;
/** The number of bytes generated by the kick-assembler code. */
private RValue bytes ;
/** The number of cycles used by the generated kick-assembler code. */
private RValue cycles ;
/** Declared clobber for the inline kick-assembler . */
2020-07-28 20:40:24 +00:00
private CpuClobber declaredClobber ;
2020-07-06 15:40:15 +00:00
2020-06-22 22:22:56 +00:00
public KickAsm ( String kickAsmCode ) {
this . kickAsmCode = kickAsmCode ;
this . uses = new ArrayList < > ( ) ;
}
}
@Override
2020-06-27 22:16:25 +00:00
public Object visitKasmContent ( KickCParser . KasmContentContext ctx ) {
2020-06-22 22:22:56 +00:00
String kasmBody = ctx . KICKASM_BODY ( ) . getText ( ) ;
2018-07-07 11:55:15 +00:00
Pattern p = Pattern . compile ( " \\ { \\ {[ \\ s]*(.*)[ \\ s]* \\ } \\ } " , Pattern . DOTALL ) ;
2020-06-22 22:22:56 +00:00
Matcher m = p . matcher ( kasmBody ) ;
2018-07-07 11:55:15 +00:00
if ( m . find ( ) ) {
2018-07-10 20:29:12 +00:00
String kickAsmCode = m . group ( 1 ) . replaceAll ( " \ r " , " " ) ;
2020-06-22 22:22:56 +00:00
KickAsm kickAsm = new KickAsm ( kickAsmCode ) ;
2019-03-19 06:48:16 +00:00
if ( ctx . asmDirectives ( ) ! = null ) {
List < AsmDirective > asmDirectives = this . visitAsmDirectives ( ctx . asmDirectives ( ) ) ;
for ( AsmDirective asmDirective : asmDirectives ) {
2020-06-21 22:26:44 +00:00
if ( asmDirective instanceof AsmDirectiveBytes ) {
2020-06-22 22:22:56 +00:00
kickAsm . bytes = ( ( AsmDirectiveBytes ) asmDirective ) . getBytes ( ) ;
2019-03-19 06:48:16 +00:00
} else if ( asmDirective instanceof AsmDirectiveCycles ) {
2020-06-22 22:22:56 +00:00
kickAsm . cycles = ( ( AsmDirectiveCycles ) asmDirective ) . getCycles ( ) ;
2019-03-19 06:48:16 +00:00
} else if ( asmDirective instanceof AsmDirectiveUses ) {
2020-06-22 22:22:56 +00:00
kickAsm . uses . add ( ( ( AsmDirectiveUses ) asmDirective ) . getUses ( ) ) ;
2019-03-19 06:48:16 +00:00
} else if ( asmDirective instanceof AsmDirectiveClobber ) {
2020-06-22 22:22:56 +00:00
kickAsm . declaredClobber = ( ( AsmDirectiveClobber ) asmDirective ) . getClobber ( ) ;
2019-03-19 06:48:16 +00:00
} else {
2020-06-22 22:22:56 +00:00
throw new CompileError ( " kickasm does not support directive " + asmDirective , StatementSource . kickAsm ( ctx ) ) ;
2018-07-25 08:10:46 +00:00
}
}
}
2020-06-22 22:22:56 +00:00
return kickAsm ;
2018-07-07 17:23:38 +00:00
}
return null ;
}
2019-03-19 06:48:16 +00:00
private interface AsmDirective {
2018-08-05 15:28:02 +00:00
}
2018-07-25 08:10:46 +00:00
@Override
2019-03-19 06:48:16 +00:00
public List < AsmDirective > visitAsmDirectives ( KickCParser . AsmDirectivesContext ctx ) {
ArrayList < AsmDirective > asmDirectives = new ArrayList < > ( ) ;
List < KickCParser . AsmDirectiveContext > params = ctx . asmDirective ( ) ;
for ( KickCParser . AsmDirectiveContext param : params ) {
AsmDirective directive = ( AsmDirective ) visit ( param ) ;
2018-08-05 15:28:02 +00:00
if ( directive ! = null ) {
2019-03-19 06:48:16 +00:00
asmDirectives . add ( directive ) ;
2018-07-25 08:10:46 +00:00
}
}
2019-03-19 06:48:16 +00:00
return asmDirectives ;
2018-07-25 08:10:46 +00:00
}
2019-03-15 23:33:16 +00:00
/** KickAssembler directive specifying a constant used by the kickasm code. */
2019-03-19 06:48:16 +00:00
public static class AsmDirectiveUses implements AsmDirective {
2019-03-15 23:33:16 +00:00
/** constant/variable used by the KickAssembler-code. */
2019-08-09 20:38:33 +00:00
private SymbolRef uses ;
2019-03-15 23:33:16 +00:00
2019-08-09 20:38:33 +00:00
public SymbolRef getUses ( ) {
2019-03-15 23:33:16 +00:00
return uses ;
}
2019-08-09 20:38:33 +00:00
AsmDirectiveUses ( SymbolRef uses ) {
2019-03-15 23:33:16 +00:00
this . uses = uses ;
}
2019-08-09 20:38:33 +00:00
public void setUses ( SymbolRef uses ) {
2019-03-15 23:33:16 +00:00
this . uses = uses ;
}
2019-03-19 06:48:16 +00:00
@Override
public String toString ( ) {
return " uses " ;
}
2019-03-15 23:33:16 +00:00
}
@Override
2021-08-10 22:55:08 +00:00
public Object visitAsmDirectiveName ( KickCParser . AsmDirectiveNameContext ctx ) {
if ( " uses " . equals ( ctx . NAME ( 0 ) . getText ( ) ) ) {
String varName = ctx . NAME ( 1 ) . getText ( ) ;
SymbolRef variableRef ;
Symbol symbol = getCurrentScope ( ) . findSymbol ( varName ) ;
if ( symbol ! = null ) {
//Found an existing variable
variableRef = symbol . getRef ( ) ;
} else {
// Either forward reference or a non-existing variable. Create a forward reference for later resolving.
variableRef = new ForwardVariableRef ( varName ) ;
}
return new AsmDirectiveUses ( variableRef ) ;
2019-03-15 23:33:16 +00:00
}
2021-08-10 22:55:08 +00:00
throw new CompileError ( " Unknown ASM directive ' " + ctx . NAME ( 0 ) . getText ( ) + " ' " , new StatementSource ( ctx ) ) ;
2019-03-19 06:48:16 +00:00
}
2021-08-10 22:55:08 +00:00
/** KickAssembler directive specifying the number of bytes for generated code/data. */
public static class AsmDirectiveBytes implements AsmDirective {
/** bytes for the KickAssembler-code. */
private final RValue bytes ;
AsmDirectiveBytes ( RValue bytes ) {
this . bytes = bytes ;
}
public RValue getBytes ( ) {
return bytes ;
}
@Override
public String toString ( ) {
return " bytes " ;
}
}
2019-03-19 06:48:16 +00:00
/** KickAssembler directive specifying the number of cycles for generated code/data. */
public static class AsmDirectiveCycles implements AsmDirective {
/** cycles for the KickAssembler-code. */
2021-05-01 11:08:08 +00:00
private final RValue cycles ;
2019-03-19 06:48:16 +00:00
2019-04-15 21:13:15 +00:00
AsmDirectiveCycles ( RValue cycles ) {
2019-03-19 06:48:16 +00:00
this . cycles = cycles ;
}
public RValue getCycles ( ) {
return cycles ;
}
@Override
public String toString ( ) {
return " cycles " ;
}
2019-03-15 23:33:16 +00:00
}
2018-07-25 17:59:49 +00:00
@Override
2021-08-10 22:55:08 +00:00
public Object visitAsmDirectiveExpr ( KickCParser . AsmDirectiveExprContext ctx ) {
if ( " cycles " . equals ( ctx . NAME ( ) . getText ( ) ) ) {
if ( ctx . expr ( ) ! = null ) {
RValue cycles = ( RValue ) this . visit ( ctx . expr ( ) ) ;
return new AsmDirectiveCycles ( cycles ) ;
}
} else if ( " bytes " . equals ( ctx . NAME ( ) . getText ( ) ) ) {
if ( ctx . expr ( ) ! = null ) {
RValue bytes = ( RValue ) this . visit ( ctx . expr ( ) ) ;
return new AsmDirectiveBytes ( bytes ) ;
}
2018-08-22 22:24:32 +00:00
}
2021-08-10 22:55:08 +00:00
throw new CompileError ( " Unknown ASM directive ' " + ctx . NAME ( ) . getText ( ) + " ' " , new StatementSource ( ctx ) ) ;
2018-07-07 11:55:15 +00:00
}
2019-03-19 06:48:16 +00:00
/** ASM Directive specifying clobber registers. */
2019-10-17 09:13:46 +00:00
private static class AsmDirectiveClobber implements AsmDirective {
2023-04-06 20:46:28 +00:00
private final CpuClobber clobber ;
2019-03-19 06:48:16 +00:00
2020-07-28 20:40:24 +00:00
AsmDirectiveClobber ( CpuClobber clobber ) {
2019-03-19 06:48:16 +00:00
this . clobber = clobber ;
}
2020-07-28 20:40:24 +00:00
public CpuClobber getClobber ( ) {
2019-03-19 06:48:16 +00:00
return clobber ;
}
@Override
public String toString ( ) {
return " clobbers " ;
}
}
@Override
2021-08-10 22:55:08 +00:00
public Object visitAsmDirectiveString ( KickCParser . AsmDirectiveStringContext ctx ) {
if ( " clobbers " . equals ( ctx . NAME ( ) . getText ( ) ) ) {
String clobberString = ctx . STRING ( ) . getText ( ) . toUpperCase ( Locale . ENGLISH ) ;
clobberString = clobberString . substring ( 1 , clobberString . length ( ) - 1 ) ;
if ( ! clobberString . matches ( " [AXY]* " ) ) {
throw new CompileError ( " Illegal clobber value " + clobberString , new StatementSource ( ctx ) ) ;
}
CpuClobber clobber = new CpuClobber ( clobberString ) ;
return new AsmDirectiveClobber ( clobber ) ;
} else if ( " resource " . equals ( ctx . NAME ( ) . getText ( ) ) ) {
TerminalNode resource = ctx . STRING ( ) ;
2021-09-25 20:04:47 +00:00
String resourceFileName = resource . getText ( ) ;
resourceFileName = resourceFileName . substring ( 1 , resourceFileName . length ( ) - 1 ) ;
addResourceFile ( ctx , resourceFileName ) ;
2021-08-10 22:55:08 +00:00
return null ;
2019-03-19 06:48:16 +00:00
}
2021-08-10 22:55:08 +00:00
throw new CompileError ( " Unknown ASM directive ' " + ctx . NAME ( ) . getText ( ) + " ' " , new StatementSource ( ctx ) ) ;
2019-03-19 06:48:16 +00:00
}
2021-09-25 20:04:47 +00:00
/ * *
* Add a resource to the program .
* @param ctx The parser context used for finding the folder containing the current source line .
* @param resourceFileName The file name of the resource file .
* /
private void addResourceFile ( ParserRuleContext ctx , String resourceFileName ) {
Path currentPath = cParser . getSourceFolderPath ( ctx ) ;
2023-12-19 08:58:08 +00:00
Path resourceFile = SourceLoader . loadFile ( resourceFileName , currentPath , new ArrayList < > ( ) ) . toPath ( ) ;
2021-09-25 20:04:47 +00:00
if ( resourceFile = = null )
throw new CompileError ( " File not found " + resourceFileName ) ;
2023-12-19 08:58:08 +00:00
if ( ! program . getAsmResourceFiles ( ) . contains ( resourceFile ) )
program . addAsmResourceFile ( resourceFile ) ;
2021-09-25 20:04:47 +00:00
if ( program . getLog ( ) . isVerboseParse ( ) ) {
2023-12-19 08:58:08 +00:00
program . getLog ( ) . append ( " Added resource " + resourceFile . toString ( ) . replace ( '\\' , '/' ) ) ;
2021-09-25 20:04:47 +00:00
}
}
2021-05-11 21:11:51 +00:00
/** Information about a declared parameter. */
static class ParameterDecl {
final public String name ;
final public SymbolType type ;
2023-11-18 09:40:50 +00:00
final public List < Directive > directives ;
2021-05-11 21:11:51 +00:00
2023-11-18 09:40:50 +00:00
public ParameterDecl ( String name , SymbolType type , List < Directive > directives ) {
2021-05-11 21:11:51 +00:00
this . name = name ;
this . type = type ;
2023-11-18 09:40:50 +00:00
this . directives = directives ;
2021-05-11 21:11:51 +00:00
}
}
2020-03-27 09:53:42 +00:00
/ * *
2021-05-11 21:11:51 +00:00
* Holds type directives , comments etc . while parsing a variable or procedure declaration .
2020-03-27 09:53:42 +00:00
* Has three levels of information pushed on top of each other :
* < ol >
* < li > Struct Member Declaration ( true while inside inside a struct declaration ) < / li >
* < li > Type information and directives ( the type ) < / li >
2021-05-11 21:11:51 +00:00
* < li > Information about parameters ( for procedures ) < / li >
2020-03-27 09:53:42 +00:00
* < / ol >
* < p >
* When parsing a declaration such as < code > volatile char a , * const b , c [ ] < / code > the type level holds < code > volatile char < / code >
* and the variable level holds the pointer / array - information and the const - declaration for b .
* /
static class VariableDeclaration {
/** State specifying that we are currently populating struct members. */
private boolean structMember = false ;
2020-03-29 18:22:16 +00:00
/** Holds directives that are not part of the type-spec (all other than const & volatile) when descending into a Variable Declaration. (type level) */
private List < Directive > declDirectives = null ;
2020-03-27 09:53:42 +00:00
/** Holds the declared comments when descending into a Variable Declaration. (type level) */
2020-03-29 18:22:16 +00:00
private List < Comment > declComments = null ;
/** The declared type (type level) */
2021-05-01 11:08:08 +00:00
private SymbolType declType ;
2020-03-29 18:22:16 +00:00
/** The declared type (variable level) */
2021-05-01 11:08:08 +00:00
private SymbolType varDeclType ;
2021-05-02 12:28:03 +00:00
/** The variable name (variable level) */
private String varName ;
2021-05-11 21:11:51 +00:00
/** The declared parameters (if this is a procedure). */
private List < ParameterDecl > parameters ;
2020-03-29 22:12:03 +00:00
2020-03-27 09:53:42 +00:00
/ * *
2020-03-29 18:22:16 +00:00
* Exits the type layer ( clears everything except struct information )
2020-03-27 09:53:42 +00:00
* /
2020-03-27 20:08:18 +00:00
void exitType ( ) {
2020-03-29 18:22:16 +00:00
this . declDirectives = null ;
this . declComments = null ;
2021-05-01 11:08:08 +00:00
this . declType = null ;
2020-03-29 18:22:16 +00:00
this . varDeclType = null ;
2021-05-02 12:28:03 +00:00
this . varName = null ;
2021-05-11 21:11:51 +00:00
this . parameters = new ArrayList < > ( ) ;
2020-03-27 09:53:42 +00:00
}
/ * *
* Exits the variable layer ( clears variable information )
* /
2020-03-27 20:08:18 +00:00
void exitVar ( ) {
2020-03-29 18:22:16 +00:00
this . varDeclType = null ;
2021-05-02 12:28:03 +00:00
this . varName = null ;
2021-05-11 21:11:51 +00:00
this . parameters = new ArrayList < > ( ) ;
2020-03-29 18:22:16 +00:00
}
2020-03-27 20:08:18 +00:00
SymbolType getEffectiveType ( ) {
2021-05-01 11:08:08 +00:00
if ( this . varDeclType ! = null )
return this . varDeclType ;
if ( this . declType ! = null )
return this . declType ;
2020-03-29 18:22:16 +00:00
return null ;
2020-03-27 09:53:42 +00:00
}
2020-03-29 18:22:16 +00:00
/ * *
* Set type - level directives . Splits directives into type - directives ( const , volatile ) and general directives ( all other ) .
*
* @param directives The directives
* /
void setDeclDirectives ( List < Directive > directives ) {
this . declDirectives = new ArrayList < > ( ) ;
for ( Directive directive : directives ) {
2021-04-24 06:05:16 +00:00
if ( directive instanceof Directive . Volatile ) {
2021-05-01 11:08:08 +00:00
// Type-qualifier directive volatile
this . declType = this . declType . getQualified ( true , this . declType . isNomodify ( ) ) ;
2021-04-24 06:05:16 +00:00
} else if ( directive instanceof Directive . Const ) {
2021-05-01 11:08:08 +00:00
// Type-qualifier directive const
this . declType = this . declType . getQualified ( this . declType . isVolatile ( ) , true ) ;
2024-01-02 20:14:21 +00:00
} else if ( directive instanceof Directive . AsmExportDirective ) {
// Type-qualifier directive volatile
this . declDirectives . add ( directive ) ;
2024-04-26 09:40:14 +00:00
this . declType = this . declType . getQualified ( false , this . declType . isNomodify ( ) ) ;
2024-01-02 20:14:21 +00:00
} else if ( directive instanceof Directive . AsmImportDirective ) {
// Type-qualifier directive volatile
this . declDirectives . add ( directive ) ;
2024-04-26 09:40:14 +00:00
this . declType = this . declType . getQualified ( false , this . declType . isNomodify ( ) ) ;
2020-03-29 18:22:16 +00:00
} else {
2021-05-01 11:08:08 +00:00
// variable directive
2020-03-30 11:25:52 +00:00
if ( ! this . declDirectives . contains ( directive ) )
this . declDirectives . add ( directive ) ;
2020-03-29 18:22:16 +00:00
}
}
2020-03-27 09:53:42 +00:00
}
2021-05-01 11:08:08 +00:00
private void setVarDeclTypeAndDirectives ( SymbolType type , List < Directive > typeDirectives ) {
for ( Directive directive : typeDirectives ) {
if ( directive instanceof Directive . Const )
type = type . getQualified ( type . isVolatile ( ) , true ) ;
if ( directive instanceof Directive . Volatile )
type = type . getQualified ( true , type . isNomodify ( ) ) ;
2020-03-29 18:22:16 +00:00
}
2021-05-01 11:08:08 +00:00
setVarDeclType ( type ) ;
}
2021-05-02 12:28:03 +00:00
public String getVarName ( ) {
return varName ;
}
public void setVarName ( String varName ) {
this . varName = varName ;
}
2021-05-01 11:08:08 +00:00
List < Directive > getDeclDirectives ( ) {
return declDirectives ;
2020-03-27 09:53:42 +00:00
}
2020-05-29 19:57:19 +00:00
List < Comment > getDeclComments ( ) {
2020-03-29 18:22:16 +00:00
return declComments ;
2020-03-27 09:53:42 +00:00
}
2020-03-27 20:08:18 +00:00
boolean isStructMember ( ) {
2020-03-27 09:53:42 +00:00
return structMember ;
}
2020-03-29 18:22:16 +00:00
public void setDeclType ( SymbolType type ) {
2021-05-01 11:08:08 +00:00
this . declType = type ;
2020-03-27 09:53:42 +00:00
}
2021-05-01 11:08:08 +00:00
void setVarDeclType ( SymbolType varDeclType ) {
2020-03-29 18:22:16 +00:00
this . varDeclType = varDeclType ;
2020-03-27 09:53:42 +00:00
}
2020-05-29 19:57:19 +00:00
void setDeclComments ( List < Comment > declComments ) {
2020-03-29 18:22:16 +00:00
this . declComments = declComments ;
2020-03-27 09:53:42 +00:00
}
2020-03-29 18:22:16 +00:00
void setStructMember ( boolean structMember ) {
this . structMember = structMember ;
2020-03-27 09:53:42 +00:00
}
2021-05-11 21:11:51 +00:00
public void setParameters ( List < ParameterDecl > parameters ) {
this . parameters = parameters ;
}
public List < ParameterDecl > getParameters ( ) {
return parameters ;
}
2020-03-27 09:53:42 +00:00
}
2020-03-29 22:12:03 +00:00
/** The current variable declaration. This is not on the stack. */
2020-03-27 09:53:42 +00:00
private VariableDeclaration varDecl = new VariableDeclaration ( ) ;
2019-04-15 21:13:15 +00:00
2020-03-29 22:12:03 +00:00
/** The stack of variable type / directive declarataions being handled. Pushed/popped when entering into a nested type declaration (eg. struct member or a cast inside an initializer) */
2021-05-01 11:08:08 +00:00
private final Deque < VariableDeclaration > varDeclStack = new LinkedList < > ( ) ;
2020-03-29 22:12:03 +00:00
/ * *
* Push the current type declaration handler onto the stack and initialize a new empty current type declaration handler .
* /
2020-05-29 19:57:19 +00:00
private void varDeclPush ( ) {
2020-03-29 22:12:03 +00:00
varDeclStack . push ( varDecl ) ;
varDecl = new VariableDeclaration ( ) ;
}
/ * *
* Discard the current type declaration handler and pop the last one fron the stack .
* /
2020-05-29 19:57:19 +00:00
private void varDeclPop ( ) {
2020-03-29 22:12:03 +00:00
this . varDecl = varDeclStack . pop ( ) ;
}
2019-04-15 22:22:47 +00:00
/ * *
* Visit the type / directive part of a declaration . Setup the local decl - variables
2019-05-30 21:50:26 +00:00
*
2019-04-15 22:22:47 +00:00
* @param ctx The declaration type & directives
* @return null
* /
2017-12-01 22:45:09 +00:00
@Override
2020-03-27 21:23:04 +00:00
public Object visitDeclType ( KickCParser . DeclTypeContext ctx ) {
2020-03-27 09:53:42 +00:00
varDecl . exitType ( ) ;
2020-03-29 22:12:03 +00:00
visit ( ctx . type ( ) ) ;
varDecl . setDeclDirectives ( getDirectives ( ctx . directive ( ) ) ) ;
2020-03-29 18:22:16 +00:00
varDecl . setDeclComments ( getCommentsSymbol ( ctx . getParent ( ) ) ) ;
2019-04-15 22:22:47 +00:00
return null ;
}
@Override
public Object visitDeclVariables ( KickCParser . DeclVariablesContext ctx ) {
2020-03-27 21:23:04 +00:00
this . visit ( ctx . declType ( ) ) ;
2021-05-02 12:28:03 +00:00
this . visit ( ctx . declaratorInitList ( ) ) ;
2020-03-27 09:53:42 +00:00
varDecl . exitType ( ) ;
2019-04-15 21:13:15 +00:00
return null ;
}
@Override
2021-05-02 12:28:03 +00:00
public Object visitDeclaratorInitList ( KickCParser . DeclaratorInitListContext ctx ) {
if ( ctx . declaratorInitList ( ) ! = null )
this . visit ( ctx . declaratorInitList ( ) ) ;
this . visit ( ctx . declaratorInit ( ) ) ;
2020-03-27 20:08:18 +00:00
varDecl . exitVar ( ) ;
2019-04-15 21:13:15 +00:00
return null ;
}
@Override
2019-06-30 13:48:31 +00:00
public Object visitDeclVariableInitExpr ( KickCParser . DeclVariableInitExprContext ctx ) {
2021-05-02 12:28:03 +00:00
this . visit ( ctx . declarator ( ) ) ;
String varName = varDecl . getVarName ( ) ;
2020-03-26 08:13:48 +00:00
KickCParser . ExprContext initializer = ctx . expr ( ) ;
2019-12-26 08:51:41 +00:00
StatementSource declSource = new StatementSource ( ( ParserRuleContext ) ctx . parent . parent ) ;
2019-11-22 22:41:01 +00:00
try {
2020-03-29 22:12:03 +00:00
final SymbolType effectiveType = varDecl . getEffectiveType ( ) ;
2021-05-11 21:11:51 +00:00
if ( effectiveType instanceof SymbolTypeProcedure ) {
2021-05-12 06:58:10 +00:00
// Declare the procedure
boolean defineProcedure = varDecl . getDeclDirectives ( ) . stream ( ) . anyMatch ( directive - > directive instanceof Directive . Intrinsic ) ;
declareProcedure ( defineProcedure , ctx , declSource ) ;
2021-05-11 21:11:51 +00:00
varDecl . exitVar ( ) ;
2020-04-11 19:15:34 +00:00
} else {
2021-05-11 21:11:51 +00:00
final boolean isStructMember = varDecl . isStructMember ( ) ;
final List < Directive > effectiveDirectives = varDecl . getDeclDirectives ( ) ;
final List < Comment > declComments = varDecl . getDeclComments ( ) ;
varDecl . exitVar ( ) ;
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( varName , getCurrentScope ( ) , false , false , effectiveType , effectiveDirectives , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2021-05-11 21:11:51 +00:00
Variable variable = varBuilder . build ( ) ;
if ( isStructMember & & ( initializer ! = null ) )
2021-08-10 15:48:55 +00:00
throw new CompileError ( " Initializer not supported inside structs " + effectiveType . toCDecl ( ) , declSource ) ;
2021-05-11 21:11:51 +00:00
if ( variable . isDeclarationOnly ( ) ) {
if ( initializer ! = null ) {
throw new CompileError ( " Initializer not allowed for extern variables " + varName , declSource ) ;
2020-06-27 18:59:59 +00:00
}
2021-05-11 21:11:51 +00:00
} else {
// Create a proper initializer
if ( initializer ! = null )
PrePostModifierHandler . addPreModifiers ( this , initializer , declSource ) ;
RValue initValue = ( initializer = = null ) ? null : ( RValue ) visit ( initializer ) ;
initValue = Initializers . constantify ( initValue , new Initializers . ValueTypeSpec ( effectiveType ) , program , declSource ) ;
boolean isPermanent = ScopeRef . ROOT . equals ( variable . getScope ( ) . getRef ( ) ) | | variable . isPermanent ( ) ;
if ( variable . isKindConstant ( ) | | ( isPermanent & & variable . isKindLoadStore ( ) & & Variable . MemoryArea . MAIN_MEMORY . equals ( variable . getMemoryArea ( ) ) & & initValue instanceof ConstantValue & & ! isStructMember & & variable . getRegister ( ) = = null ) ) {
// Set initial value
ConstantValue constInitValue = getConstInitValue ( initValue , initializer , declSource ) ;
variable . setInitValue ( constInitValue ) ;
// Add comments to constant
variable . setComments ( ensureUnusedComments ( declComments ) ) ;
} else if ( ! variable . isKindConstant ( ) & & ! isStructMember ) {
2021-07-27 17:11:14 +00:00
// The previous assignment of an intermediate variable that can be modified instead of creating a new statement
StatementLValue previousAssignment = null ;
2023-04-06 20:46:28 +00:00
if ( initValue instanceof VariableRef initVarRef ) {
2021-07-27 17:11:14 +00:00
if ( initVarRef . isIntermediate ( ) ) {
Statement previousStatement = getPreviousStatement ( ) ;
if ( previousStatement instanceof StatementLValue & & ( ( StatementLValue ) previousStatement ) . getlValue ( ) . equals ( initVarRef ) ) {
previousAssignment = ( StatementLValue ) previousStatement ;
}
}
}
Statement initStmt ;
2021-08-08 11:47:48 +00:00
if ( previousAssignment ! = null ) {
2021-07-27 17:11:14 +00:00
previousAssignment . setlValue ( variable . getVariableRef ( ) ) ;
previousAssignment . setInitialAssignment ( true ) ;
previousAssignment . setSource ( declSource ) ;
initStmt = previousAssignment ;
} else {
initStmt = new StatementAssignment ( variable . getVariableRef ( ) , initValue , true , declSource , Comment . NO_COMMENTS ) ;
addStatement ( initStmt ) ;
}
2021-05-11 21:11:51 +00:00
if ( variable . getScope ( ) . getRef ( ) . equals ( ScopeRef . ROOT ) ) {
// Add comments to variable for global vars
variable . setComments ( ensureUnusedComments ( declComments ) ) ;
} else {
// Add comments to statement for local vars
initStmt . setComments ( ensureUnusedComments ( declComments ) ) ;
}
2020-06-27 18:59:59 +00:00
2021-05-11 21:11:51 +00:00
}
if ( initializer ! = null )
PrePostModifierHandler . addPostModifiers ( this , initializer , declSource ) ;
2020-04-11 19:15:34 +00:00
}
2020-02-25 20:38:29 +00:00
}
2019-11-22 22:41:01 +00:00
} catch ( CompileError e ) {
2019-12-26 08:51:41 +00:00
throw new CompileError ( e . getMessage ( ) , declSource ) ;
2019-11-22 22:41:01 +00:00
}
2020-03-26 08:13:48 +00:00
return null ;
}
2019-11-22 22:41:01 +00:00
/ * *
2020-03-26 08:13:48 +00:00
* Ensure that the initializer value is a constant . Fail if it is not
2019-11-22 22:41:01 +00:00
*
2020-01-01 17:27:53 +00:00
* @param initValue The initializer value ( result from { @link Initializers # constantify ( RValue , Initializers . ValueTypeSpec , Program , StatementSource ) }
2019-12-22 17:19:20 +00:00
* @param initializer The initializer
* @param statementSource The source line
* @return The constant initializer value
2019-11-22 22:41:01 +00:00
* /
2019-12-22 17:19:20 +00:00
private ConstantValue getConstInitValue ( RValue initValue , KickCParser . ExprContext initializer , StatementSource statementSource ) {
2019-11-22 22:41:01 +00:00
if ( initializer ! = null & & PrePostModifierHandler . hasPrePostModifiers ( this , initializer , statementSource ) ) {
throw new CompileError ( " Constant value contains a pre/post-modifier. " , statementSource ) ;
2019-11-03 19:11:06 +00:00
}
2019-12-22 10:53:37 +00:00
if ( initValue instanceof ForwardVariableRef ) {
2023-04-06 20:46:28 +00:00
throw new CompileError ( " Variable used before being defined " + initValue , statementSource ) ;
2019-11-23 17:31:00 +00:00
}
2019-12-22 17:19:20 +00:00
if ( ! ( initValue instanceof ConstantValue ) )
2021-01-20 18:02:32 +00:00
throw new CompileError ( " Initializer is not a constant value. " , statementSource ) ;
2019-12-22 17:19:20 +00:00
return ( ConstantValue ) initValue ;
2019-06-30 13:48:31 +00:00
}
@Override
public Object visitDeclVariableInitKasm ( KickCParser . DeclVariableInitKasmContext ctx ) {
2021-05-02 12:28:03 +00:00
this . visit ( ctx . declarator ( ) ) ;
String varName = varDecl . getVarName ( ) ;
2019-11-22 22:41:01 +00:00
StatementSource statementSource = new StatementSource ( ctx ) ;
2021-04-12 18:57:46 +00:00
SymbolType effectiveType = this . varDecl . getEffectiveType ( ) ;
if ( ! ( effectiveType instanceof SymbolTypePointer ) | | ( ( SymbolTypePointer ) effectiveType ) . getArraySpec ( ) = = null ) {
2021-08-10 15:48:55 +00:00
throw new CompileError ( " KickAsm initializers only supported for arrays " + varDecl . getEffectiveType ( ) . toCDecl ( ) , statementSource ) ;
2019-06-30 21:08:39 +00:00
}
// Add KickAsm statement
2020-06-27 22:16:25 +00:00
KickAsm kasm = ( KickAsm ) this . visit ( ctx . kasmContent ( ) ) ;
2020-06-22 22:22:56 +00:00
if ( kasm . cycles ! = null ) {
2019-11-22 22:41:01 +00:00
throw new CompileError ( " KickAsm initializers does not support 'cycles' directive. " , statementSource ) ;
2019-06-30 21:08:39 +00:00
}
2020-06-22 22:22:56 +00:00
if ( kasm . bytes ! = null ) {
2019-11-22 22:41:01 +00:00
throw new CompileError ( " KickAsm initializers does not support 'bytes' directive. " , statementSource ) ;
2019-06-30 21:08:39 +00:00
}
2020-06-22 22:22:56 +00:00
if ( kasm . declaredClobber ! = null ) {
2019-11-22 22:41:01 +00:00
throw new CompileError ( " KickAsm initializers does not support 'clobbers' directive. " , statementSource ) ;
2019-06-30 21:08:39 +00:00
}
2021-04-12 18:57:46 +00:00
ConstantArrayKickAsm constantArrayKickAsm = new ConstantArrayKickAsm ( ( ( SymbolTypePointer ) varDecl . getEffectiveType ( ) ) . getElementType ( ) , kasm . kickAsmCode , kasm . uses , ( ( SymbolTypePointer ) effectiveType ) . getArraySpec ( ) . getArraySize ( ) ) ;
2019-11-22 22:41:01 +00:00
// Add a constant variable
Scope scope = getCurrentScope ( ) ;
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( varName , scope , false , false , varDecl . getEffectiveType ( ) , varDecl . getDeclDirectives ( ) , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2019-12-22 21:04:30 +00:00
Variable variable = varBuilder . build ( ) ;
// Set constant value
variable . setInitValue ( getConstInitValue ( constantArrayKickAsm , null , statementSource ) ) ;
2019-11-22 22:41:01 +00:00
// Add comments to constant
2020-03-29 18:22:16 +00:00
variable . setComments ( ensureUnusedComments ( varDecl . getDeclComments ( ) ) ) ;
2020-03-27 09:53:42 +00:00
varDecl . exitVar ( ) ;
2019-11-22 22:41:01 +00:00
return null ;
2017-05-07 18:32:30 +00:00
}
2019-04-15 21:13:15 +00:00
/ * *
* Find the directives in the parse tree
2019-05-30 21:50:26 +00:00
*
2019-04-15 21:13:15 +00:00
* @param directivesCtx The directives in the parse tree to examine
* @return Objects representing the found directives
* /
private List < Directive > getDirectives ( List < KickCParser . DirectiveContext > directivesCtx ) {
List < Directive > directives = new ArrayList < > ( ) ;
for ( KickCParser . DirectiveContext directiveContext : directivesCtx ) {
directives . add ( ( Directive ) this . visit ( directiveContext ) ) ;
}
return directives ;
}
2018-04-29 19:39:12 +00:00
/ * *
* Add declared directives to a procedure .
*
* @param procedure The procedure
2019-04-15 22:22:47 +00:00
* @param directives The directives to add
2018-04-29 19:39:12 +00:00
* /
2023-04-06 20:46:28 +00:00
private void addDirectives ( Procedure procedure , List < Directive > directives ) {
2018-04-29 19:39:12 +00:00
for ( Directive directive : directives ) {
2019-10-18 21:56:20 +00:00
if ( directive instanceof Directive . Inline ) {
2018-04-29 19:39:12 +00:00
procedure . setDeclaredInline ( true ) ;
2020-02-27 23:11:33 +00:00
procedure . setCallingConvention ( Procedure . CallingConvention . PHI_CALL ) ;
2023-04-23 09:54:47 +00:00
} else if ( directive instanceof Directive . Bank directiveBank ) {
Bank bank = new Bank ( directiveBank . getBankArea ( ) , directiveBank . getBankNumber ( ) ) ;
procedure . setBank ( bank ) ;
2023-11-24 14:46:48 +00:00
} else if ( directive instanceof Directive . AsmImportDirective ) {
procedure . setAsmImportLibrary ( ( ( Directive . AsmImportDirective ) directive ) . getAsmLibrary ( ) ) ;
program . addAsmImportLibrary ( ( ( Directive . AsmImportDirective ) directive ) . getAsmLibrary ( ) ) ;
} else if ( directive instanceof Directive . AsmExportDirective ) {
procedure . setAsmExportLibrary ( ( ( Directive . AsmExportDirective ) directive ) . getAsmLibrary ( ) ) ;
2019-10-18 21:56:20 +00:00
} else if ( directive instanceof Directive . CallingConvention ) {
2020-02-27 06:22:22 +00:00
procedure . setCallingConvention ( ( ( Directive . CallingConvention ) directive ) . callingConvention ) ;
2019-10-18 21:56:20 +00:00
} else if ( directive instanceof Directive . Interrupt ) {
procedure . setInterruptType ( ( ( Directive . Interrupt ) directive ) . interruptType ) ;
} else if ( directive instanceof Directive . ReserveZp ) {
procedure . setReservedZps ( ( ( Directive . ReserveZp ) directive ) . reservedZp ) ;
2020-04-19 21:29:37 +00:00
} else if ( directive instanceof Directive . Intrinsic ) {
procedure . setDeclaredIntrinsic ( true ) ;
2023-10-27 13:31:51 +00:00
} else if ( directive instanceof Directive . Extern ) {
procedure . setDeclaredExtern ( true ) ;
2024-01-16 05:01:05 +00:00
// TODO: check this ...
2021-05-02 12:28:03 +00:00
//} else {
// throw new CompileError("Unsupported function directive " + directive.getName(), source);
2018-01-30 21:18:49 +00:00
}
}
2017-12-29 17:20:11 +00:00
}
2018-08-19 19:48:26 +00:00
/ * *
* Add declared directives to a conditional jump ( as part of a loop ) .
*
* @param conditional The loop conditional
* @param directivesCtx The directives to add
* /
2019-11-03 16:05:55 +00:00
private void addDirectives ( StatementConditionalJump
conditional , List < KickCParser . DirectiveContext > directivesCtx ) {
2019-04-15 21:13:15 +00:00
List < Directive > directives = getDirectives ( directivesCtx ) ;
2018-08-19 19:48:26 +00:00
for ( Directive directive : directives ) {
StatementSource source = new StatementSource ( directivesCtx . get ( 0 ) ) ;
2019-10-18 21:56:20 +00:00
if ( directive instanceof Directive . Inline ) {
2018-08-19 19:48:26 +00:00
conditional . setDeclaredUnroll ( true ) ;
} else {
2020-12-05 22:56:01 +00:00
throw new CompileError ( " Unsupported loop directive " + directive . getName ( ) , source ) ;
2018-08-19 19:48:26 +00:00
}
}
}
2017-12-29 17:20:11 +00:00
@Override
public Directive visitDirectiveConst ( KickCParser . DirectiveConstContext ctx ) {
2019-11-02 21:54:13 +00:00
return new Directive . Const ( ) ;
2019-10-20 18:01:38 +00:00
}
2018-04-29 19:39:12 +00:00
@Override
public Object visitDirectiveInline ( KickCParser . DirectiveInlineContext ctx ) {
2019-10-18 21:56:20 +00:00
return new Directive . Inline ( ) ;
2018-04-29 19:39:12 +00:00
}
2022-11-15 16:32:04 +00:00
@Override
2022-11-21 17:17:35 +00:00
public Object visitDirectiveBank ( KickCParser . DirectiveBankContext ctx ) {
2023-04-23 09:54:47 +00:00
String bankArea = ctx . NAME ( ) . getText ( ) ;
Number bankNumber = NumberParser . parseLiteral ( ctx . NUMBER ( ) . getText ( ) ) ;
return new Directive . Bank ( bankArea , bankNumber . longValue ( ) ) ;
2022-11-15 16:32:04 +00:00
}
2020-04-19 21:29:37 +00:00
@Override
public Object visitDirectiveIntrinsic ( KickCParser . DirectiveIntrinsicContext ctx ) {
return new Directive . Intrinsic ( ) ;
}
2018-08-04 11:07:21 +00:00
@Override
public Object visitDirectiveInterrupt ( KickCParser . DirectiveInterruptContext ctx ) {
2018-09-26 20:44:40 +00:00
String interruptType ;
2018-08-05 15:28:02 +00:00
if ( ctx . getChildCount ( ) > 1 ) {
2020-12-21 00:04:34 +00:00
interruptType = ctx . getChild ( 2 ) . getText ( ) . toLowerCase ( Locale . ENGLISH ) ;
2018-09-26 20:44:40 +00:00
} else {
2020-12-21 00:04:34 +00:00
interruptType = currentInterruptType ;
2018-08-05 15:28:02 +00:00
}
2020-12-21 00:04:34 +00:00
return new Directive . Interrupt ( interruptType ) ;
2018-08-04 11:07:21 +00:00
}
2023-11-24 14:46:48 +00:00
public Object visitDirectiveAsmImport ( KickCParser . DirectiveAsmImportContext ctx ) {
String asmImport = ctx . pragmaParam ( ) . getText ( ) . toLowerCase ( Locale . ENGLISH ) ;
2023-12-12 07:20:06 +00:00
asmImport = asmImport . substring ( 1 , asmImport . length ( ) - 1 ) ;
2023-11-24 14:46:48 +00:00
this . program . addAsmImportLibrary ( asmImport ) ;
return new Directive . AsmImportDirective ( asmImport ) ;
}
public Object visitDirectiveAsmExport ( KickCParser . DirectiveAsmExportContext ctx ) {
2024-01-01 15:50:19 +00:00
String asmExport = program . getAsmLibraryName ( ) ;
2024-01-02 18:41:03 +00:00
// if(asmExport == null) {
// throw new CompileError("__asm_export directive used before #pragma asm_library declaration.", new StatementSource(ctx));
// }
2024-01-02 20:14:21 +00:00
2023-11-24 14:46:48 +00:00
return new Directive . AsmExportDirective ( asmExport ) ;
2023-10-27 13:31:51 +00:00
}
2019-09-16 23:11:51 +00:00
@Override
public Directive visitDirectiveCallingConvention ( KickCParser . DirectiveCallingConventionContext ctx ) {
2023-11-24 14:46:48 +00:00
Procedure . CallingConvention callingConvention = Procedure . CallingConvention . getCallingConvention ( ctx . getText ( ) ) ;
2020-02-27 06:22:22 +00:00
return new Directive . CallingConvention ( callingConvention ) ;
2019-09-16 23:11:51 +00:00
}
2017-12-29 17:20:11 +00:00
@Override
public Directive visitDirectiveAlign ( KickCParser . DirectiveAlignContext ctx ) {
Number alignment = NumberParser . parseLiteral ( ctx . NUMBER ( ) . getText ( ) ) ;
2019-10-18 21:56:20 +00:00
return new Directive . Align ( alignment . intValue ( ) ) ;
2017-12-29 17:20:11 +00:00
}
2018-01-30 10:50:18 +00:00
@Override
public Directive visitDirectiveRegister ( KickCParser . DirectiveRegisterContext ctx ) {
2019-10-13 20:09:33 +00:00
String name = null ;
2019-08-12 19:46:01 +00:00
if ( ctx . NAME ( ) ! = null ) {
2019-10-13 20:09:33 +00:00
name = ctx . NAME ( ) . getText ( ) ;
2019-08-10 06:39:42 +00:00
}
2019-11-02 21:54:13 +00:00
if ( name ! = null )
return new Directive . NamedRegister ( name ) ;
else
return new Directive . Register ( ) ;
2018-01-30 10:50:18 +00:00
}
2019-10-17 09:13:46 +00:00
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveMemoryAreaZp ( KickCParser . DirectiveMemoryAreaZpContext ctx ) {
2023-12-17 10:39:53 +00:00
if ( ctx . NUMBER ( ) ! = null )
2023-11-18 09:40:50 +00:00
try {
2023-12-17 10:39:53 +00:00
String zpText = ctx . NUMBER ( ) . getText ( ) ;
2023-11-18 09:40:50 +00:00
Number zpNumber = NumberParser . parseLiteral ( zpText ) ;
return new Directive . MemZp ( zpNumber . intValue ( ) ) ;
} catch ( NumberFormatException e ) {
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
}
else
return new Directive . MemZp ( ) ;
2019-10-17 09:13:46 +00:00
}
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveMemoryAreaMain ( KickCParser . DirectiveMemoryAreaMainContext ctx ) {
return new Directive . MemMain ( ) ;
2019-10-17 09:13:46 +00:00
}
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveMemoryAreaAddress ( KickCParser . DirectiveMemoryAreaAddressContext ctx ) {
2019-10-17 09:13:46 +00:00
try {
2020-12-09 11:18:07 +00:00
KickCParser . ExprContext initializer = ctx . expr ( ) ;
RValue initValue = ( initializer = = null ) ? null : ( RValue ) visit ( initializer ) ;
StatementSource statementSource = new StatementSource ( ctx ) ;
ConstantValue addressAsConstantValue = getConstInitValue ( initValue , initializer , statementSource ) ;
2023-04-06 20:46:28 +00:00
ConstantLiteral < ? > literal = addressAsConstantValue . calculateLiteral ( program . getScope ( ) ) ;
2020-12-09 11:18:07 +00:00
if ( literal instanceof ConstantInteger ) {
Long address = ( ( ConstantInteger ) literal ) . getValue ( ) ;
2020-12-11 00:18:36 +00:00
return new Directive . Address ( addressAsConstantValue , address ) ;
2020-12-09 11:18:07 +00:00
} else {
throw new CompileError ( " __address is not an integer : " + initValue . toString ( program ) , new StatementSource ( ctx ) ) ;
}
2019-10-17 09:13:46 +00:00
} catch ( NumberFormatException e ) {
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
2019-09-24 05:58:48 +00:00
}
}
2019-10-20 11:44:30 +00:00
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveFormSsa ( KickCParser . DirectiveFormSsaContext ctx ) {
return new Directive . FormSsa ( ) ;
2019-10-20 11:44:30 +00:00
}
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveFormMa ( KickCParser . DirectiveFormMaContext ctx ) {
return new Directive . FormMa ( ) ;
2019-10-20 11:44:30 +00:00
}
2018-08-05 20:40:13 +00:00
@Override
public Directive visitDirectiveVolatile ( KickCParser . DirectiveVolatileContext ctx ) {
2019-11-02 21:54:13 +00:00
return new Directive . Volatile ( ) ;
2018-08-05 20:40:13 +00:00
}
2020-02-13 10:36:27 +00:00
@Override
public Object visitDirectiveStatic ( KickCParser . DirectiveStaticContext ctx ) {
return new Directive . Static ( ) ;
}
2020-04-09 07:21:43 +00:00
@Override
public Object visitDirectiveExtern ( KickCParser . DirectiveExternContext ctx ) {
return new Directive . Extern ( ) ;
}
2019-08-10 07:02:35 +00:00
@Override
2019-11-02 21:54:13 +00:00
public Directive visitDirectiveExport ( KickCParser . DirectiveExportContext ctx ) {
2019-10-18 21:56:20 +00:00
return new Directive . Export ( ) ;
2019-08-10 07:02:35 +00:00
}
2019-04-19 23:44:54 +00:00
@Override
2019-08-08 11:10:23 +00:00
public Directive visitDirectiveReserveZp ( KickCParser . DirectiveReserveZpContext ctx ) {
2020-08-25 23:24:04 +00:00
final List < KickCParser . PragmaParamContext > reserveParams = ctx . pragmaParam ( ) ;
final List < Integer > reservedZps = pragmaParamRanges ( reserveParams ) ;
2019-10-18 21:56:20 +00:00
return new Directive . ReserveZp ( reservedZps ) ;
2019-04-19 23:44:54 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtSeq ( KickCParser . StmtSeqContext ctx ) {
2017-12-27 22:53:51 +00:00
for ( int i = 0 ; i < ctx . getChildCount ( ) ; i + + ) {
2017-05-07 18:32:30 +00:00
this . visit ( ctx . stmt ( i ) ) ;
}
return null ;
}
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtBlock ( KickCParser . StmtBlockContext ctx ) {
2017-12-27 22:53:51 +00:00
if ( ctx . stmtSeq ( ) ! = null ) {
2019-03-31 15:38:21 +00:00
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
2019-03-29 23:15:53 +00:00
scopeStack . push ( blockScope ) ;
2017-07-13 18:41:16 +00:00
this . visit ( ctx . stmtSeq ( ) ) ;
2019-03-29 23:15:53 +00:00
scopeStack . pop ( ) ;
2017-07-13 18:41:16 +00:00
}
2017-05-07 18:32:30 +00:00
return null ;
}
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtExpr ( KickCParser . StmtExprContext ctx ) {
2019-07-08 14:43:09 +00:00
PrePostModifierHandler . addPreModifiers ( this , ctx . commaExpr ( ) , new StatementSource ( ctx ) ) ;
2019-08-25 19:37:10 +00:00
beginNotConsumedTracking ( ) ;
RValue exprVal = ( RValue ) this . visit ( ctx . commaExpr ( ) ) ;
if ( notConsumed ( exprVal ) ) {
// Make a tmpVar to create the statement
2020-06-21 08:23:04 +00:00
Variable tmpVar = addIntermediateVar ( ) ;
2019-08-25 19:37:10 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
RValue rVal = exprVal ;
if ( exprVal instanceof LValue ) {
rVal = copyLValue ( ( LValue ) exprVal ) ;
}
2020-06-15 20:23:06 +00:00
addStatement ( new StatementAssignment ( ( LValue ) tmpVar . getRef ( ) , rVal , true , new StatementSource ( ctx ) , comments ) ) ;
2019-08-25 19:37:10 +00:00
}
endNotConsumedTracking ( ) ;
2019-07-08 14:43:09 +00:00
PrePostModifierHandler . addPostModifiers ( this , ctx . commaExpr ( ) , new StatementSource ( ctx ) ) ;
2017-05-07 18:32:30 +00:00
return null ;
}
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtIfElse ( KickCParser . StmtIfElseContext ctx ) {
2018-04-21 19:38:30 +00:00
KickCParser . StmtContext ifStmt = ctx . stmt ( 0 ) ;
KickCParser . StmtContext elseStmt = ctx . stmt ( 1 ) ;
2020-04-12 08:15:32 +00:00
RValue rValue = addCondition ( ctx . commaExpr ( ) , StatementSource . ifThen ( ctx ) ) ;
2019-02-17 14:50:42 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
2018-07-21 09:13:32 +00:00
if ( elseStmt = = null ) {
2018-04-21 19:38:30 +00:00
// If without else - skip the entire section if condition not met
2020-06-21 08:23:04 +00:00
SymbolVariableRef notExprVar = addIntermediateVar ( ) . getRef ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementAssignment ( ( LValue ) notExprVar , null , Operators . LOGIC_NOT , rValue , true , StatementSource . ifThen ( ctx ) , comments ) ) ;
2019-03-31 15:38:21 +00:00
Label endJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementConditionalJump ( notExprVar , endJumpLabel . getRef ( ) , StatementSource . ifThen ( ctx ) , Comment . NO_COMMENTS ) ) ;
2018-04-21 19:38:30 +00:00
this . visit ( ifStmt ) ;
// No else statement - just add the label
2020-06-15 20:23:06 +00:00
addStatement ( new StatementLabel ( endJumpLabel . getRef ( ) , StatementSource . ifThen ( ctx ) , Comment . NO_COMMENTS ) ) ;
2018-04-21 19:38:30 +00:00
} else {
// If with else - jump to if section if condition met - fall into else otherwise.
2019-03-31 15:38:21 +00:00
Label ifJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementConditionalJump ( rValue , ifJumpLabel . getRef ( ) , StatementSource . ifThen ( ctx ) , comments ) ) ;
2018-04-21 19:38:30 +00:00
// Add else body
2017-05-07 18:32:30 +00:00
this . visit ( elseStmt ) ;
2018-04-21 19:38:30 +00:00
// There is an else statement - add the if part and any needed labels/jumps
2019-03-31 15:38:21 +00:00
Label endJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementJump ( endJumpLabel . getRef ( ) , StatementSource . ifElse ( ctx ) , Comment . NO_COMMENTS ) ) ;
addStatement ( new StatementLabel ( ifJumpLabel . getRef ( ) , StatementSource . ifThen ( ctx ) , Comment . NO_COMMENTS ) ) ;
2018-04-21 19:38:30 +00:00
this . visit ( ifStmt ) ;
2019-07-08 14:43:09 +00:00
StatementLabel endJumpTarget = new StatementLabel ( endJumpLabel . getRef ( ) , StatementSource . ifThen ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( endJumpTarget ) ;
2017-05-07 18:32:30 +00:00
}
return null ;
}
2019-03-31 15:38:21 +00:00
/** A loop being generated. */
static class Loop {
/** The scope of the loop. */
Scope loopScope ;
/** The label after the loop that a break will jump to. Null if no break has been encountered. */
Label breakLabel ;
2019-03-31 18:15:01 +00:00
/** The label that a continue will jump to. Null if no continue has been encountered. */
Label continueLabel ;
2019-08-25 13:01:04 +00:00
/** true if the loop is a switch-statement. */
boolean isSwitch ;
2019-10-16 23:27:56 +00:00
Loop ( Scope loopScope , boolean isSwitch ) {
2019-03-31 15:38:21 +00:00
this . loopScope = loopScope ;
2019-08-25 13:01:04 +00:00
this . isSwitch = isSwitch ;
2019-03-31 15:38:21 +00:00
}
2019-04-15 21:13:15 +00:00
Label getBreakLabel ( ) {
2019-03-31 15:38:21 +00:00
return breakLabel ;
}
2019-04-15 21:13:15 +00:00
Label getOrCreateBreakLabel ( ) {
2019-04-08 14:26:39 +00:00
if ( breakLabel = = null ) {
2019-03-31 15:38:21 +00:00
breakLabel = loopScope . addLabelIntermediate ( ) ;
}
return breakLabel ;
}
2019-03-31 18:15:01 +00:00
2019-04-15 21:13:15 +00:00
Label getContinueLabel ( ) {
2019-03-31 18:15:01 +00:00
return continueLabel ;
}
2019-04-15 21:13:15 +00:00
void setContinueLabel ( Label continueLabel ) {
2019-03-31 18:15:01 +00:00
this . continueLabel = continueLabel ;
}
2019-04-15 21:13:15 +00:00
Label getOrCreateContinueLabel ( ) {
2019-04-08 14:26:39 +00:00
if ( continueLabel = = null ) {
2019-03-31 18:15:01 +00:00
continueLabel = loopScope . addLabelIntermediate ( ) ;
}
return continueLabel ;
}
2019-03-31 15:38:21 +00:00
}
/** The loops being generated. */
2020-12-21 00:04:34 +00:00
private final Stack < Loop > loopStack = new Stack < > ( ) ;
2019-03-31 15:38:21 +00:00
2017-05-07 18:32:30 +00:00
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtWhile ( KickCParser . StmtWhileContext ctx ) {
2019-03-31 15:38:21 +00:00
// Create the block scope early - to keep all statements of the loop inside it
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
scopeStack . push ( blockScope ) ;
2019-08-25 13:01:04 +00:00
loopStack . push ( new Loop ( blockScope , false ) ) ;
2019-03-31 15:38:21 +00:00
Label beginJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label doJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label endJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2019-02-17 14:50:42 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
2019-07-08 14:43:09 +00:00
StatementLabel beginJumpTarget = new StatementLabel ( beginJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , comments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( beginJumpTarget ) ;
2020-04-12 08:15:32 +00:00
RValue rValue = addCondition ( ctx . commaExpr ( ) , StatementSource . whileDo ( ctx ) ) ;
2019-07-08 14:43:09 +00:00
StatementConditionalJump doJmpStmt = new StatementConditionalJump ( rValue , doJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( doJmpStmt ) ;
2019-07-08 14:43:09 +00:00
Statement endJmpStmt = new StatementJump ( endJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( endJmpStmt ) ;
2019-07-08 14:43:09 +00:00
StatementLabel doJumpTarget = new StatementLabel ( doJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( doJumpTarget ) ;
2019-03-31 18:15:01 +00:00
// Reuse the begin jump target for continue.
loopStack . peek ( ) . setContinueLabel ( beginJumpLabel ) ;
2019-03-31 15:38:21 +00:00
addLoopBody ( ctx . stmt ( ) ) ;
2019-07-08 14:43:09 +00:00
Statement beginJmpStmt = new StatementJump ( beginJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( beginJmpStmt ) ;
2019-07-08 14:43:09 +00:00
StatementLabel endJumpTarget = new StatementLabel ( endJumpLabel . getRef ( ) , StatementSource . whileDo ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( endJumpTarget ) ;
2018-08-19 19:48:26 +00:00
// Add directives
addDirectives ( doJmpStmt , ctx . directive ( ) ) ;
2019-03-31 15:38:21 +00:00
addLoopBreakLabel ( loopStack . pop ( ) , ctx ) ;
scopeStack . pop ( ) ;
2017-05-07 18:32:30 +00:00
return null ;
}
2020-04-12 08:15:32 +00:00
/ * *
* Add code to evaluate a comma - expr condition ( used in while / for / . . . ) .
2020-04-19 21:29:37 +00:00
*
2020-04-12 08:15:32 +00:00
* @param conditionCtx The comma - expr condition to evaluate
* @param statementSource The statement source used for errors
* @return The RValue of the condition
* /
private RValue addCondition ( KickCParser . CommaExprContext conditionCtx , StatementSource statementSource ) {
// Add any pre-modifiers
PrePostModifierHandler . addPreModifiers ( this , conditionCtx , statementSource ) ;
RValue rValue = ( RValue ) this . visit ( conditionCtx ) ;
// Add any post-modifiers
2020-07-06 15:40:15 +00:00
if ( PrePostModifierHandler . hasPostModifiers ( this , conditionCtx , statementSource ) ) {
2020-04-12 08:15:32 +00:00
// If modifiers are present the RValue must be assigned before the post-modifier is executed
2020-07-06 15:40:15 +00:00
if ( ! ( rValue instanceof VariableRef ) | | ! ( ( VariableRef ) rValue ) . isIntermediate ( ) ) {
2020-04-12 08:15:32 +00:00
// Make a new temporary variable and assign that
2020-06-21 08:23:04 +00:00
Variable tmpVar = addIntermediateVar ( ) ;
2020-04-12 08:15:32 +00:00
Statement stmtExpr = new StatementAssignment ( tmpVar . getVariableRef ( ) , rValue , true , statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmtExpr ) ;
2020-04-12 08:15:32 +00:00
rValue = tmpVar . getRef ( ) ;
}
PrePostModifierHandler . addPostModifiers ( this , conditionCtx , statementSource ) ;
}
return rValue ;
}
2017-05-21 10:56:11 +00:00
@Override
2017-05-23 06:51:39 +00:00
public Void visitStmtDoWhile ( KickCParser . StmtDoWhileContext ctx ) {
2019-03-29 23:15:53 +00:00
// Create the block scope early - to keep all statements of the loop inside it
2019-03-31 15:38:21 +00:00
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
scopeStack . push ( blockScope ) ;
2019-08-25 13:01:04 +00:00
loopStack . push ( new Loop ( blockScope , false ) ) ;
2019-02-17 14:50:42 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
2019-03-31 15:38:21 +00:00
Label beginJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2019-07-08 14:43:09 +00:00
StatementLabel beginJumpTarget = new StatementLabel ( beginJumpLabel . getRef ( ) , StatementSource . doWhile ( ctx ) , comments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( beginJumpTarget ) ;
2019-03-31 15:38:21 +00:00
addLoopBody ( ctx . stmt ( ) ) ;
2019-03-31 18:15:01 +00:00
addLoopContinueLabel ( loopStack . peek ( ) , ctx ) ;
2020-04-12 08:15:32 +00:00
RValue rValue = addCondition ( ctx . commaExpr ( ) , StatementSource . doWhile ( ctx ) ) ;
2019-07-08 14:43:09 +00:00
StatementConditionalJump doJmpStmt = new StatementConditionalJump ( rValue , beginJumpLabel . getRef ( ) , StatementSource . doWhile ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( doJmpStmt ) ;
2018-08-19 19:48:26 +00:00
addDirectives ( doJmpStmt , ctx . directive ( ) ) ;
2019-03-31 15:38:21 +00:00
addLoopBreakLabel ( loopStack . pop ( ) , ctx ) ;
2019-03-29 23:15:53 +00:00
scopeStack . pop ( ) ;
2017-05-21 10:56:11 +00:00
return null ;
}
2019-08-11 21:59:29 +00:00
@Override
public Object visitStmtSwitch ( KickCParser . StmtSwitchContext ctx ) {
2019-08-21 15:33:11 +00:00
Loop containingLoop = null ;
if ( ! loopStack . isEmpty ( ) ) {
containingLoop = loopStack . peek ( ) ;
}
2019-08-11 22:18:48 +00:00
// Create a block scope - to keep all statements of the loop inside it
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
scopeStack . push ( blockScope ) ;
2019-08-25 13:01:04 +00:00
Loop switchLoop = new Loop ( blockScope , true ) ;
2019-08-21 15:33:11 +00:00
if ( containingLoop ! = null ) {
switchLoop . setContinueLabel ( containingLoop . getOrCreateContinueLabel ( ) ) ;
}
2019-08-18 19:55:21 +00:00
loopStack . push ( switchLoop ) ;
2019-08-11 22:18:48 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
// TODO: Add comments to next stmt
// Evaluate the switch-expression
2020-04-12 08:15:32 +00:00
RValue eValue = addCondition ( ctx . commaExpr ( ) , StatementSource . switchExpr ( ctx ) ) ;
2019-08-12 19:46:01 +00:00
// Add case conditional jumps
List < SwitchCaseBody > caseBodies = new ArrayList < > ( ) ;
for ( KickCParser . SwitchCaseContext caseContext : ctx . switchCases ( ) . switchCase ( ) ) {
List < Comment > caseComments = ensureUnusedComments ( getCommentsSymbol ( caseContext ) ) ;
RValue cValue = ( RValue ) this . visit ( caseContext . expr ( ) ) ;
Label cJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
caseBodies . add ( new SwitchCaseBody ( cJumpLabel , caseContext . stmtSeq ( ) , StatementSource . switchCase ( caseContext ) ) ) ;
StatementConditionalJump cJmpStmt = new StatementConditionalJump ( eValue , Operators . EQ , cValue , cJumpLabel . getRef ( ) , StatementSource . switchCase ( caseContext ) , caseComments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( cJmpStmt ) ;
2019-08-12 19:46:01 +00:00
}
// Add default ending jump
Label dJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
StatementJump dJmpStmt = new StatementJump ( dJumpLabel . getRef ( ) , StatementSource . switchDefault ( ctx . switchCases ( ) ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( dJmpStmt ) ;
2019-08-12 19:46:01 +00:00
// Add case labels & bodies
for ( SwitchCaseBody caseBody : caseBodies ) {
StatementLabel cJumpTarget = new StatementLabel ( caseBody . cJumpLabel . getRef ( ) , caseBody . statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( cJumpTarget ) ;
2019-08-12 19:46:01 +00:00
if ( caseBody . stmtSequence ! = null ) {
this . visit ( caseBody . stmtSequence ) ;
}
}
// Add default label
StatementLabel dJumpTarget = new StatementLabel ( dJumpLabel . getRef ( ) , StatementSource . switchDefault ( ctx . switchCases ( ) ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( dJumpTarget ) ;
2019-08-12 19:46:01 +00:00
if ( ctx . switchCases ( ) . stmtSeq ( ) ! = null ) {
this . visit ( ctx . switchCases ( ) . stmtSeq ( ) ) ;
}
2019-08-11 22:18:48 +00:00
addLoopBreakLabel ( loopStack . pop ( ) , ctx ) ;
scopeStack . pop ( ) ;
return null ;
2019-08-11 21:59:29 +00:00
}
2019-08-12 19:46:01 +00:00
/** Holds the body of a switch() case. */
public static class SwitchCaseBody {
/** Label for the statments of the case. */
Label cJumpLabel ;
/** Statments of the case. */
KickCParser . StmtSeqContext stmtSequence ;
/** Source of the case. */
StatementSource statementSource ;
2019-08-24 12:30:21 +00:00
SwitchCaseBody ( Label cJumpLabel , KickCParser . StmtSeqContext stmtSequence , StatementSource statementSource ) {
2019-08-12 19:46:01 +00:00
this . cJumpLabel = cJumpLabel ;
this . stmtSequence = stmtSequence ;
this . statementSource = statementSource ;
}
}
2017-08-17 22:28:39 +00:00
@Override
public Object visitStmtFor ( KickCParser . StmtForContext ctx ) {
2019-04-15 22:22:47 +00:00
this . visit ( ctx . forLoop ( ) ) ;
2017-08-17 22:28:39 +00:00
return null ;
}
@Override
public Object visitForClassic ( KickCParser . ForClassicContext ctx ) {
2019-03-31 15:38:21 +00:00
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
2019-03-29 23:15:53 +00:00
scopeStack . push ( blockScope ) ;
2019-08-25 13:01:04 +00:00
loopStack . push ( new Loop ( blockScope , false ) ) ;
2019-08-07 08:56:39 +00:00
// Add initialization
2019-04-15 22:22:47 +00:00
this . visit ( ctx . forClassicInit ( ) ) ;
2017-08-17 22:28:39 +00:00
KickCParser . StmtForContext stmtForCtx = ( KickCParser . StmtForContext ) ctx . getParent ( ) ;
// Add label
2019-08-07 08:56:39 +00:00
Label beginJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label doJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label endJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2019-02-17 16:53:19 +00:00
List < Comment > comments = getCommentsSymbol ( stmtForCtx ) ;
2019-08-07 08:56:39 +00:00
StatementLabel repeatTarget = new StatementLabel ( beginJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , comments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( repeatTarget ) ;
2019-08-07 08:56:39 +00:00
// Add condition
2020-07-07 11:07:26 +00:00
KickCParser . ForClassicConditionContext conditionCtx = ctx . forClassicCondition ( ) ;
2020-07-07 06:32:42 +00:00
RValue conditionRvalue = null ;
2020-08-23 22:35:48 +00:00
if ( conditionCtx ! = null ) {
2020-07-07 11:07:26 +00:00
conditionRvalue = addCondition ( conditionCtx . commaExpr ( ) , StatementSource . forClassic ( ctx ) ) ;
2020-07-07 06:32:42 +00:00
}
2019-08-07 08:56:39 +00:00
// Add jump if condition was met
2020-07-07 06:32:42 +00:00
Statement doJmpStmt ;
2020-08-23 22:35:48 +00:00
if ( conditionRvalue ! = null ) {
2020-07-07 06:32:42 +00:00
doJmpStmt = new StatementConditionalJump ( conditionRvalue , doJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
addStatement ( doJmpStmt ) ;
Statement endJmpStmt = new StatementJump ( endJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
addStatement ( endJmpStmt ) ;
} else {
// No condition - loop forever
2020-08-23 22:35:48 +00:00
doJmpStmt = new StatementJump ( doJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
2020-07-07 06:32:42 +00:00
addStatement ( doJmpStmt ) ;
}
2019-08-07 08:56:39 +00:00
StatementLabel doJumpTarget = new StatementLabel ( doJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( doJumpTarget ) ;
2017-08-17 22:28:39 +00:00
// Add body
2019-03-31 15:38:21 +00:00
addLoopBody ( stmtForCtx . stmt ( ) ) ;
2017-08-17 22:28:39 +00:00
// Add increment
2019-08-10 13:07:51 +00:00
addLoopContinueLabel ( loopStack . peek ( ) , ctx ) ;
2020-07-07 11:07:26 +00:00
KickCParser . CommaExprContext incrementCtx = ctx . commaExpr ( ) ;
2019-08-01 19:15:33 +00:00
if ( incrementCtx ! = null ) {
2020-07-06 15:40:15 +00:00
PrePostModifierHandler . addPreModifiers ( this , incrementCtx , StatementSource . forClassic ( ctx ) ) ;
this . visit ( incrementCtx ) ;
PrePostModifierHandler . addPostModifiers ( this , incrementCtx , StatementSource . forClassic ( ctx ) ) ;
2017-10-22 23:22:36 +00:00
}
2019-08-07 08:56:39 +00:00
// Jump back to beginning
Statement beginJmpStmt = new StatementJump ( beginJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( beginJmpStmt ) ;
2019-08-07 08:56:39 +00:00
StatementLabel endJumpTarget = new StatementLabel ( endJumpLabel . getRef ( ) , StatementSource . forClassic ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( endJumpTarget ) ;
2020-07-07 06:32:42 +00:00
if ( doJmpStmt instanceof StatementConditionalJump )
addDirectives ( ( StatementConditionalJump ) doJmpStmt , stmtForCtx . directive ( ) ) ;
2019-03-31 15:38:21 +00:00
addLoopBreakLabel ( loopStack . pop ( ) , ctx ) ;
2019-03-29 23:15:53 +00:00
scopeStack . pop ( ) ;
2017-08-17 22:28:39 +00:00
return null ;
}
2019-04-15 22:22:47 +00:00
@Override
public Object visitForClassicInitDecl ( KickCParser . ForClassicInitDeclContext ctx ) {
2019-05-30 21:50:26 +00:00
if ( ctx . declVariables ( ) ! = null ) {
2019-04-15 22:22:47 +00:00
this . visit ( ctx . declVariables ( ) ) ;
}
return null ;
}
2017-08-17 22:28:39 +00:00
@Override
public Object visitForRange ( KickCParser . ForRangeContext ctx ) {
2019-03-31 15:38:21 +00:00
BlockScope blockScope = getCurrentScope ( ) . addBlockScope ( ) ;
2019-03-29 23:15:53 +00:00
scopeStack . push ( blockScope ) ;
2019-08-25 13:01:04 +00:00
loopStack . push ( new Loop ( blockScope , false ) ) ;
2019-07-08 14:43:09 +00:00
StatementSource statementSource = StatementSource . forRanged ( ctx ) ;
2021-05-02 15:39:07 +00:00
this . visit ( ctx . declType ( ) ) ;
this . visit ( ctx . declarator ( ) ) ;
2020-03-27 09:53:42 +00:00
SymbolType varType = varDecl . getEffectiveType ( ) ;
2021-05-02 15:39:07 +00:00
String varName = varDecl . getVarName ( ) ;
2019-11-01 19:46:10 +00:00
Variable lValue ;
2019-04-15 22:22:47 +00:00
if ( varType ! = null ) {
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( varName , blockScope , false , false , varType , varDecl . getDeclDirectives ( ) , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2019-12-22 21:04:30 +00:00
lValue = varBuilder . build ( ) ;
2019-04-15 22:22:47 +00:00
} else {
2020-04-09 20:17:33 +00:00
lValue = getCurrentScope ( ) . findVariable ( varName ) ;
2019-10-13 20:09:33 +00:00
if ( lValue = = null ) {
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Loop variable not declared " + varName , statementSource ) ;
2019-10-03 08:41:10 +00:00
}
2019-04-15 22:22:47 +00:00
}
2020-03-29 18:22:16 +00:00
boolean initialAssignment = ( varDecl . getEffectiveType ( ) ! = null ) ;
2020-03-27 09:53:42 +00:00
varDecl . exitType ( ) ;
2019-04-15 22:22:47 +00:00
KickCParser . StmtForContext stmtForCtx = ( KickCParser . StmtForContext ) ctx . getParent ( ) ;
2017-08-17 22:28:39 +00:00
KickCParser . ExprContext rangeFirstCtx = ctx . expr ( 0 ) ;
KickCParser . ExprContext rangeLastCtx = ctx . expr ( 1 ) ;
// Assign loop variable with first value
2018-07-12 12:42:45 +00:00
RValue rangeLastValue = ( RValue ) visit ( rangeLastCtx ) ;
RValue rangeFirstValue = ( RValue ) visit ( rangeFirstCtx ) ;
2019-05-30 21:50:26 +00:00
if ( varType ! = null ) {
2019-06-02 22:44:46 +00:00
if ( rangeFirstValue instanceof ConstantInteger ) ( ( ConstantInteger ) rangeFirstValue ) . setType ( varType ) ;
if ( rangeLastValue instanceof ConstantInteger ) ( ( ConstantInteger ) rangeLastValue ) . setType ( varType ) ;
2019-05-18 10:23:13 +00:00
}
2019-12-24 10:05:32 +00:00
Statement stmtInit = new StatementAssignment ( ( LValue ) lValue . getRef ( ) , rangeFirstValue , initialAssignment , statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmtInit ) ;
2017-08-17 22:28:39 +00:00
// Add label
2019-02-17 16:53:19 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( stmtForCtx ) ) ;
2019-03-31 15:38:21 +00:00
Label repeatLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2019-07-08 14:43:09 +00:00
StatementLabel repeatTarget = new StatementLabel ( repeatLabel . getRef ( ) , statementSource , comments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( repeatTarget ) ;
2017-08-17 22:28:39 +00:00
// Add body
2019-03-31 15:38:21 +00:00
addLoopBody ( stmtForCtx . stmt ( ) ) ;
2019-03-31 18:15:01 +00:00
addLoopContinueLabel ( loopStack . peek ( ) , ctx ) ;
2017-08-17 22:28:39 +00:00
// Add increment
2019-12-24 10:05:32 +00:00
Statement stmtNxt = new StatementAssignment ( ( LValue ) lValue . getRef ( ) , lValue . getRef ( ) , Operators . PLUS , new RangeNext ( rangeFirstValue , rangeLastValue ) , false , statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmtNxt ) ;
2018-05-14 08:31:43 +00:00
// Add condition i!=last+1 or i!=last-1
2018-07-12 12:42:45 +00:00
RValue beyondLastVal = new RangeComparison ( rangeFirstValue , rangeLastValue , lValue . getType ( ) ) ;
2020-06-21 08:23:04 +00:00
Variable tmpVar = addIntermediateVar ( ) ;
2019-11-01 17:24:09 +00:00
SymbolVariableRef tmpVarRef = tmpVar . getRef ( ) ;
2019-12-24 10:05:32 +00:00
Statement stmtTmpVar = new StatementAssignment ( ( LValue ) tmpVarRef , lValue . getRef ( ) , Operators . NEQ , beyondLastVal , true , statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmtTmpVar ) ;
2017-08-17 22:28:39 +00:00
// Add jump if condition was met
2019-07-08 14:43:09 +00:00
StatementConditionalJump doJmpStmt = new StatementConditionalJump ( tmpVarRef , repeatLabel . getRef ( ) , statementSource , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( doJmpStmt ) ;
2018-08-19 19:48:26 +00:00
addDirectives ( doJmpStmt , stmtForCtx . directive ( ) ) ;
2019-03-31 15:38:21 +00:00
addLoopBreakLabel ( loopStack . pop ( ) , ctx ) ;
2019-03-29 23:15:53 +00:00
scopeStack . pop ( ) ;
2017-08-17 22:28:39 +00:00
return null ;
}
2019-03-31 15:38:21 +00:00
private void addLoopBreakLabel ( Loop loop , ParserRuleContext ctx ) {
2019-04-08 14:26:39 +00:00
if ( loop . getBreakLabel ( ) ! = null ) {
2019-03-31 15:38:21 +00:00
StatementLabel breakTarget = new StatementLabel ( loop . getBreakLabel ( ) . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( breakTarget ) ;
2019-03-31 15:38:21 +00:00
}
}
2019-03-31 18:15:01 +00:00
private void addLoopContinueLabel ( Loop loop , ParserRuleContext ctx ) {
2019-04-08 14:26:39 +00:00
if ( loop . getContinueLabel ( ) ! = null ) {
2019-03-31 18:15:01 +00:00
StatementLabel continueTarget = new StatementLabel ( loop . getContinueLabel ( ) . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( continueTarget ) ;
2019-03-31 18:15:01 +00:00
}
}
2019-03-31 15:38:21 +00:00
private void addLoopBody ( KickCParser . StmtContext stmt ) {
if ( stmt ! = null ) {
2023-04-06 20:46:28 +00:00
if ( stmt instanceof KickCParser . StmtBlockContext stmtBlockContext ) {
2019-03-31 15:38:21 +00:00
// Skip the block context and reuse the one created by the for() itself
if ( stmtBlockContext . stmtSeq ( ) ! = null ) {
this . visit ( stmtBlockContext . stmtSeq ( ) ) ;
}
} else {
this . visit ( stmt ) ;
}
}
}
2021-08-07 15:05:09 +00:00
@Override
public Object visitStmtLabel ( KickCParser . StmtLabelContext ctx ) {
String labelName = ctx . NAME ( ) . getText ( ) ;
2021-08-08 11:47:48 +00:00
if ( getCurrentScope ( ) . getLocalLabel ( labelName ) ! = null )
throw new CompileError ( " label already defined ' " + labelName + " '. " , new StatementSource ( ctx ) ) ;
2021-08-07 15:05:09 +00:00
Scope procedureScope = getCurrentProcedure ( ) ;
Label label = procedureScope . addLabel ( labelName ) ;
addStatement ( new StatementLabel ( label . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
return null ;
}
@Override
public Object visitStmtGoto ( KickCParser . StmtGotoContext ctx ) {
String labelName = ctx . NAME ( ) . getText ( ) ;
Label label = new Label ( labelName , getCurrentScope ( ) , false ) ;
addStatement ( new StatementJump ( label . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
return null ;
}
2019-03-31 15:38:21 +00:00
@Override
public Object visitStmtBreak ( KickCParser . StmtBreakContext ctx ) {
if ( loopStack . isEmpty ( ) ) {
throw new CompileError ( " Break not inside a loop! " , new StatementSource ( ctx ) ) ;
}
Loop currentLoop = loopStack . peek ( ) ;
Label breakLabel = currentLoop . getOrCreateBreakLabel ( ) ;
Statement breakJmpStmt = new StatementJump ( breakLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( breakJmpStmt ) ;
2019-03-31 15:38:21 +00:00
return null ;
}
@Override
public Object visitStmtContinue ( KickCParser . StmtContinueContext ctx ) {
2019-03-31 18:15:01 +00:00
if ( loopStack . isEmpty ( ) ) {
throw new CompileError ( " Continue not inside a loop! " , new StatementSource ( ctx ) ) ;
}
Loop currentLoop = loopStack . peek ( ) ;
2019-08-25 13:01:04 +00:00
Label continueLabel ;
if ( currentLoop . isSwitch ) {
continueLabel = currentLoop . getContinueLabel ( ) ;
2019-08-29 20:52:58 +00:00
if ( continueLabel = = null ) {
2019-08-25 13:01:04 +00:00
throw new CompileError ( " Continue not inside a loop! " , new StatementSource ( ctx ) ) ;
}
2019-08-29 20:52:58 +00:00
} else {
2019-08-25 13:01:04 +00:00
continueLabel = currentLoop . getOrCreateContinueLabel ( ) ;
}
2019-03-31 18:15:01 +00:00
Statement continueJmpStmt = new StatementJump ( continueLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
addStatement ( continueJmpStmt ) ;
2019-03-31 18:15:01 +00:00
return null ;
2019-03-31 15:38:21 +00:00
}
2018-07-12 12:42:45 +00:00
2017-11-05 02:01:32 +00:00
@Override
public Object visitStmtAsm ( KickCParser . StmtAsmContext ctx ) {
2019-03-15 06:59:25 +00:00
// Find all defined labels in the ASM
2019-03-18 19:54:08 +00:00
List < String > definedLabels = getAsmDefinedLabels ( ctx ) ;
2019-03-15 06:59:25 +00:00
// Find all referenced symbols in the ASM
2019-08-10 16:27:05 +00:00
Map < String , SymbolRef > referenced = getAsmReferencedSymbolVariables ( ctx , definedLabels ) ;
2019-03-15 06:59:25 +00:00
List < Comment > comments = ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ;
2019-03-18 19:54:08 +00:00
2020-07-28 20:40:24 +00:00
CpuClobber declaredClobber = null ;
2019-03-18 19:54:08 +00:00
if ( ctx . asmDirectives ( ) ! = null ) {
List < AsmDirective > asmDirectives = this . visitAsmDirectives ( ctx . asmDirectives ( ) ) ;
for ( AsmDirective asmDirective : asmDirectives ) {
if ( asmDirective instanceof AsmDirectiveClobber ) {
declaredClobber = ( ( AsmDirectiveClobber ) asmDirective ) . getClobber ( ) ;
2019-03-19 06:48:16 +00:00
} else {
2019-07-08 14:43:09 +00:00
throw new CompileError ( " inline ASM does not support directive " + asmDirective , StatementSource . asm ( ctx ) ) ;
2019-03-18 19:54:08 +00:00
}
}
}
2020-04-05 20:54:00 +00:00
StatementAsm statementAsm = new StatementAsm ( ctx . asmLines ( ) , referenced , declaredClobber , StatementSource . asm ( ctx ) , comments ) ;
2020-06-15 20:23:06 +00:00
addStatement ( statementAsm ) ;
2019-03-15 06:59:25 +00:00
return null ;
}
2019-08-03 11:22:27 +00:00
2019-03-15 06:59:25 +00:00
/ * *
* Find all referenced symbol variables
2019-03-31 15:38:21 +00:00
*
2019-03-15 06:59:25 +00:00
* @param ctx An ASM statement
* @param definedLabels All labels defined in the ASM
* @return All variables / constants referenced in the ASM . Some may be ForwardVariableRefs to be resolved later .
* /
2019-11-03 16:05:55 +00:00
private Map < String , SymbolRef > getAsmReferencedSymbolVariables ( KickCParser . StmtAsmContext
ctx , List < String > definedLabels ) {
2019-08-10 16:27:05 +00:00
Map < String , SymbolRef > referenced = new LinkedHashMap < > ( ) ;
2023-04-06 20:46:28 +00:00
KickCParserBaseVisitor < Void > findReferenced = new KickCParserBaseVisitor < > ( ) {
2019-03-14 23:02:33 +00:00
2019-03-14 23:30:14 +00:00
@Override
2019-03-15 06:59:25 +00:00
public Void visitAsmExprBinary ( KickCParser . AsmExprBinaryContext ctx ) {
ParseTree operator = ctx . getChild ( 1 ) ;
if ( operator . getText ( ) . equals ( " . " ) ) {
// Skip any . operator for now as it accesses data in another scope.
// TODO Implement checking of labels/constants in other scopes
return null ;
} else {
return super . visitAsmExprBinary ( ctx ) ;
}
2019-03-14 23:30:14 +00:00
}
2019-03-14 23:02:33 +00:00
@Override
public Void visitAsmExprLabel ( KickCParser . AsmExprLabelContext ctxLabel ) {
2019-08-25 11:07:21 +00:00
String label = ctxLabel . ASM_NAME ( ) . toString ( ) ;
2020-08-23 22:35:48 +00:00
if ( label . equalsIgnoreCase ( " x " ) | | label . equalsIgnoreCase ( " y " ) | | label . equalsIgnoreCase ( " z " ) | | label . equalsIgnoreCase ( " sp " ) )
2020-07-28 17:05:58 +00:00
// Skip registers
return super . visitAsmExprLabel ( ctxLabel ) ;
2019-03-14 23:30:14 +00:00
if ( ! definedLabels . contains ( label ) ) {
// Look for the symbol
2020-04-09 20:17:33 +00:00
Symbol symbol = getCurrentScope ( ) . findSymbol ( ctxLabel . ASM_NAME ( ) . getText ( ) ) ;
2019-08-12 19:46:01 +00:00
if ( symbol ! = null ) {
2019-08-10 16:27:05 +00:00
referenced . put ( label , symbol . getRef ( ) ) ;
2019-03-14 23:30:14 +00:00
} else {
2019-03-15 06:59:25 +00:00
// Either forward reference or a non-existing variable. Create a forward reference for later resolving.
2019-08-25 11:07:21 +00:00
referenced . put ( label , new ForwardVariableRef ( ctxLabel . ASM_NAME ( ) . getText ( ) ) ) ;
2019-03-14 23:30:14 +00:00
}
2019-03-14 23:02:33 +00:00
}
return super . visitAsmExprLabel ( ctxLabel ) ;
}
} ;
2019-03-14 23:30:14 +00:00
findReferenced . visit ( ctx . asmLines ( ) ) ;
2019-03-15 06:59:25 +00:00
return referenced ;
}
2019-03-14 23:30:14 +00:00
2019-03-15 06:59:25 +00:00
/ * *
2020-08-23 22:35:48 +00:00
* Find all labels defined in the ASM ( not multi - labels ) .
2019-03-31 15:38:21 +00:00
*
2019-03-15 06:59:25 +00:00
* @param ctx An ASM statement
* @return All labels defined in the ASM .
* /
2019-03-18 19:54:08 +00:00
private List < String > getAsmDefinedLabels ( KickCParser . StmtAsmContext ctx ) {
2019-03-15 06:59:25 +00:00
List < String > definedLabels = new ArrayList < > ( ) ;
2023-04-06 20:46:28 +00:00
KickCParserBaseVisitor < Void > findDefinedLabels = new KickCParserBaseVisitor < > ( ) {
2019-03-15 06:59:25 +00:00
@Override
public Void visitAsmLabelName ( KickCParser . AsmLabelNameContext ctx ) {
2019-08-25 11:07:21 +00:00
String label = ctx . ASM_NAME ( ) . getText ( ) ;
2019-03-15 06:59:25 +00:00
definedLabels . add ( label ) ;
return super . visitAsmLabelName ( ctx ) ;
}
} ;
findDefinedLabels . visit ( ctx . asmLines ( ) ) ;
return definedLabels ;
2017-11-05 02:01:32 +00:00
}
2017-05-23 06:51:39 +00:00
@Override
public Void visitStmtReturn ( KickCParser . StmtReturnContext ctx ) {
2017-06-13 05:18:07 +00:00
Procedure procedure = getCurrentProcedure ( ) ;
2019-04-15 21:13:15 +00:00
KickCParser . CommaExprContext exprCtx = ctx . commaExpr ( ) ;
2017-12-01 22:45:09 +00:00
RValue rValue ;
2017-12-27 22:53:51 +00:00
if ( exprCtx ! = null ) {
2020-04-13 08:50:57 +00:00
if ( SymbolType . VOID . equals ( procedure . getReturnType ( ) ) ) {
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Return value from void function " + procedure . getFullName ( ) , new StatementSource ( ctx ) ) ;
2020-04-13 08:50:57 +00:00
}
2019-07-08 14:43:09 +00:00
PrePostModifierHandler . addPreModifiers ( this , exprCtx , new StatementSource ( ctx ) ) ;
2017-06-05 14:01:50 +00:00
rValue = ( RValue ) this . visit ( exprCtx ) ;
2020-04-09 20:17:33 +00:00
Variable returnVar = procedure . getLocalVariable ( " return " ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementAssignment ( ( LValue ) returnVar . getRef ( ) , rValue , false , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
2019-07-08 14:43:09 +00:00
PrePostModifierHandler . addPostModifiers ( this , exprCtx , new StatementSource ( ctx ) ) ;
2017-06-05 14:01:50 +00:00
}
2020-04-09 20:17:33 +00:00
Label returnLabel = procedure . getLocalLabel ( SymbolRef . PROCEXIT_BLOCK_NAME ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementJump ( returnLabel . getRef ( ) , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
2017-06-05 14:01:50 +00:00
return null ;
2017-05-23 06:51:39 +00:00
}
2023-04-04 10:45:38 +00:00
@Override
public RValue visitInitUnion ( KickCParser . InitUnionContext ctx ) {
final String memberName = ctx . NAME ( ) . getText ( ) ;
final RValue rValue = ( RValue ) visit ( ctx . expr ( ) ) ;
2023-04-04 11:11:36 +00:00
return new UnionDesignator ( memberName , rValue ) ;
2023-04-04 10:45:38 +00:00
}
2017-05-23 06:51:39 +00:00
@Override
public RValue visitInitList ( KickCParser . InitListContext ctx ) {
2017-10-17 11:34:41 +00:00
List < RValue > initValues = new ArrayList < > ( ) ;
2023-04-04 10:45:38 +00:00
for ( KickCParser . ExprContext initializer : ctx . expr ( ) ) {
RValue rValue = ( RValue ) visit ( initializer ) ;
2023-04-04 07:30:40 +00:00
initValues . add ( rValue ) ;
2023-04-02 10:43:18 +00:00
}
2023-04-04 10:45:38 +00:00
return new ValueList ( initValues ) ;
2017-05-23 06:51:39 +00:00
}
@Override
2020-03-29 22:12:03 +00:00
public Object visitTypeSimple ( KickCParser . TypeSimpleContext ctx ) {
2023-04-06 20:46:28 +00:00
StringBuilder typeName = new StringBuilder ( ) ;
2021-08-01 18:52:23 +00:00
for ( TerminalNode simpleTypeNode : ctx . SIMPLETYPE ( ) ) {
2021-08-08 11:47:48 +00:00
if ( typeName . length ( ) > 0 )
2023-04-06 20:46:28 +00:00
typeName . append ( " " ) ;
typeName . append ( simpleTypeNode . getText ( ) ) ;
2021-08-01 18:52:23 +00:00
}
2023-04-06 20:46:28 +00:00
final SymbolType typeSimple = SymbolType . get ( typeName . toString ( ) ) ;
2021-08-08 11:47:48 +00:00
if ( typeSimple = = null )
throw new CompileError ( " Unknown type ' " + typeName + " ' " , new StatementSource ( ctx ) ) ;
2020-03-29 22:12:03 +00:00
varDecl . setDeclType ( typeSimple ) ;
return null ;
2017-05-23 06:51:39 +00:00
}
2019-05-27 06:39:59 +00:00
@Override
2024-01-16 05:01:05 +00:00
public Object visitStructForwardDef ( KickCParser . StructForwardDefContext ctx ) {
2021-07-22 19:05:48 +00:00
2021-08-08 11:47:48 +00:00
boolean isUnion = ctx . UNION ( ) ! = null ;
2024-01-16 05:01:05 +00:00
String structDefName ;
if ( ctx . NAME ( ) ! = null ) {
structDefName = ctx . NAME ( ) . getText ( ) ;
} else {
structDefName = program . getScope ( ) . allocateIntermediateVariableName ( ) ;
}
StructDefinition structDefinition = program . getScope ( ) . addStructDefinition ( structDefName , isUnion ) ;
varDecl . setDeclType ( structDefinition . getType ( ) ) ;
return null ;
}
@Override
public Object visitStructDefinition ( KickCParser . StructDefinitionContext ctx ) {
2021-07-22 19:05:48 +00:00
2024-01-16 05:01:05 +00:00
boolean isUnion = ctx . UNION ( ) ! = null ;
2019-05-27 06:39:59 +00:00
String structDefName ;
2019-05-30 21:50:26 +00:00
if ( ctx . NAME ( ) ! = null ) {
2019-05-27 06:39:59 +00:00
structDefName = ctx . NAME ( ) . getText ( ) ;
} else {
2020-05-16 10:40:01 +00:00
structDefName = program . getScope ( ) . allocateIntermediateVariableName ( ) ;
2019-05-27 06:39:59 +00:00
}
2021-07-22 19:05:48 +00:00
StructDefinition structDefinition = program . getScope ( ) . addStructDefinition ( structDefName , isUnion ) ;
2019-05-27 06:39:59 +00:00
scopeStack . push ( structDefinition ) ;
for ( KickCParser . StructMembersContext memberCtx : ctx . structMembers ( ) ) {
2020-03-29 22:12:03 +00:00
varDeclPush ( ) ;
varDecl . setStructMember ( true ) ;
2019-05-27 06:39:59 +00:00
visit ( memberCtx ) ;
2020-03-29 22:12:03 +00:00
varDecl . setStructMember ( false ) ;
varDeclPop ( ) ;
2019-05-27 06:39:59 +00:00
}
scopeStack . pop ( ) ;
2020-03-29 22:12:03 +00:00
varDecl . setDeclType ( structDefinition . getType ( ) ) ;
return null ;
2019-05-27 06:39:59 +00:00
}
2019-05-28 20:28:17 +00:00
@Override
public Object visitStructRef ( KickCParser . StructRefContext ctx ) {
String structDefName = ctx . NAME ( ) . getText ( ) ;
2020-04-09 20:17:33 +00:00
StructDefinition structDefinition = program . getScope ( ) . getLocalStructDefinition ( structDefName ) ;
2019-08-03 12:08:48 +00:00
if ( structDefinition = = null ) {
throw new CompileError ( " Unknown struct type " + structDefName , new StatementSource ( ctx ) ) ;
2019-06-07 11:46:36 +00:00
}
2020-03-29 22:12:03 +00:00
varDecl . setDeclType ( structDefinition . getType ( ) ) ;
return null ;
2019-05-28 20:28:17 +00:00
}
2019-06-18 22:39:15 +00:00
@Override
public Object visitTypeEnumDef ( KickCParser . TypeEnumDefContext ctx ) {
2020-03-29 22:12:03 +00:00
visit ( ctx . enumDef ( ) ) ;
return null ;
2019-06-18 22:39:15 +00:00
}
private EnumDefinition currentEnum = null ;
@Override
public Object visitEnumDef ( KickCParser . EnumDefContext ctx ) {
2019-06-18 23:53:03 +00:00
try {
String enumDefName ;
if ( ctx . NAME ( ) ! = null ) {
enumDefName = ctx . NAME ( ) . getText ( ) ;
} else {
enumDefName = getCurrentScope ( ) . allocateIntermediateVariableName ( ) ;
}
EnumDefinition enumDefinition = new EnumDefinition ( enumDefName , getCurrentScope ( ) ) ;
getCurrentScope ( ) . add ( enumDefinition ) ;
this . currentEnum = enumDefinition ;
scopeStack . push ( currentEnum ) ;
visit ( ctx . enumMemberList ( ) ) ;
scopeStack . pop ( ) ;
this . currentEnum = null ;
// Copy all members to upper-level scope
Scope parentScope = getCurrentScope ( ) ;
2021-08-08 11:47:48 +00:00
while ( parentScope instanceof StructDefinition | | parentScope instanceof TypeDefsScope )
parentScope = parentScope . getScope ( ) ;
2019-11-01 19:46:10 +00:00
for ( Variable member : enumDefinition . getAllConstants ( false ) ) {
2023-04-11 08:13:41 +00:00
parentScope . add ( Variable . createConstant ( member . getLocalName ( ) , SymbolType . BYTE , parentScope , member . getInitValue ( ) , currentSegmentData ) ) ;
2019-06-18 23:53:03 +00:00
}
2020-03-29 22:12:03 +00:00
varDecl . setDeclType ( SymbolType . BYTE ) ;
return null ;
2019-08-03 12:08:48 +00:00
} catch ( CompileError e ) {
2019-06-18 23:53:03 +00:00
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
2019-06-18 22:39:15 +00:00
}
}
@Override
public Object visitEnumMember ( KickCParser . EnumMemberContext ctx ) {
String memberName = ctx . NAME ( ) . getText ( ) ;
ConstantValue enumValue ;
2019-08-03 12:08:48 +00:00
if ( ctx . expr ( ) ! = null ) {
2019-06-18 22:39:15 +00:00
RValue exprVal = ( RValue ) visit ( ctx . expr ( ) ) ;
if ( ! ( exprVal instanceof ConstantValue ) ) {
2019-08-03 12:08:48 +00:00
throw new CompileError ( " Enum value not constant " + memberName , new StatementSource ( ctx ) ) ;
2019-06-18 22:39:15 +00:00
}
enumValue = ( ConstantValue ) exprVal ;
} else {
// No specific value - find previous value
2019-11-01 19:46:10 +00:00
List < Variable > values = new ArrayList < > ( currentEnum . getAllConstants ( false ) ) ;
2019-06-18 22:39:15 +00:00
if ( values . isEmpty ( ) ) {
enumValue = new ConstantInteger ( 0L , SymbolType . BYTE ) ;
} else {
2019-11-01 19:46:10 +00:00
Variable prevEnumMember = values . get ( values . size ( ) - 1 ) ;
2019-12-16 21:51:59 +00:00
ConstantValue prevValue = prevEnumMember . getInitValue ( ) ;
2019-06-18 22:39:15 +00:00
if ( prevValue instanceof ConstantInteger ) {
2019-08-03 12:08:48 +00:00
enumValue = new ConstantInteger ( ( ( ConstantInteger ) prevValue ) . getInteger ( ) + 1 , SymbolType . BYTE ) ;
} else {
2019-06-18 22:39:15 +00:00
enumValue = new ConstantBinary ( prevValue , Operators . PLUS , new ConstantInteger ( 1L , SymbolType . BYTE ) ) ;
}
}
}
2023-04-11 08:13:41 +00:00
currentEnum . add ( Variable . createConstant ( memberName , SymbolType . BYTE , getCurrentScope ( ) , enumValue , currentSegmentData ) ) ;
2019-06-18 22:39:15 +00:00
return null ;
}
@Override
public Object visitTypeEnumRef ( KickCParser . TypeEnumRefContext ctx ) {
2020-03-29 22:12:03 +00:00
visit ( ctx . enumRef ( ) ) ;
return null ;
2019-06-18 22:39:15 +00:00
}
@Override
public Object visitEnumRef ( KickCParser . EnumRefContext ctx ) {
String enumDefName = ctx . NAME ( ) . getText ( ) ;
2020-04-09 20:17:33 +00:00
EnumDefinition enumDefinition = program . getScope ( ) . getLocalEnumDefinition ( enumDefName ) ;
2019-08-03 12:08:48 +00:00
if ( enumDefinition = = null ) {
throw new CompileError ( " Unknown enum " + enumDefName , new StatementSource ( ctx ) ) ;
2019-06-18 22:39:15 +00:00
}
2020-03-29 22:12:03 +00:00
varDecl . setDeclType ( SymbolType . BYTE ) ;
return null ;
2019-06-18 22:39:15 +00:00
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeName ( KickCParser . TypeNameContext ctx ) {
varDeclPush ( ) ;
this . visit ( ctx . type ( ) ) ;
this . visit ( ctx . typeNameDeclarator ( ) ) ;
final SymbolType type = varDecl . getEffectiveType ( ) ;
varDeclPop ( ) ;
return type ;
}
2021-05-02 12:28:03 +00:00
@Override
public Object visitDeclaratorPointer ( KickCParser . DeclaratorPointerContext ctx ) {
final SymbolType elementDeclType = varDecl . getEffectiveType ( ) ;
SymbolTypePointer pointerType = new SymbolTypePointer ( elementDeclType ) ;
final List < Directive > typeDirectives = getDirectives ( ctx . directive ( ) ) ;
varDecl . setVarDeclTypeAndDirectives ( pointerType , typeDirectives ) ;
this . visit ( ctx . declarator ( ) ) ;
return null ;
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeNameDeclaratorPointer ( KickCParser . TypeNameDeclaratorPointerContext ctx ) {
final SymbolType elementDeclType = varDecl . getEffectiveType ( ) ;
SymbolTypePointer pointerType = new SymbolTypePointer ( elementDeclType ) ;
final List < Directive > typeDirectives = getDirectives ( ctx . directive ( ) ) ;
varDecl . setVarDeclTypeAndDirectives ( pointerType , typeDirectives ) ;
this . visit ( ctx . typeNameDeclarator ( ) ) ;
return varDecl . getEffectiveType ( ) ;
}
2021-05-02 12:28:03 +00:00
@Override
public Object visitDeclaratorArray ( KickCParser . DeclaratorArrayContext ctx ) {
this . visit ( ctx . declarator ( ) ) ;
// Handle array type declaration by updating the declared type and the array spec
ArraySpec arraySpec ;
if ( ctx . expr ( ) ! = null ) {
varDeclPush ( ) ;
RValue sizeVal = ( RValue ) visit ( ctx . expr ( ) ) ;
if ( ! ( sizeVal instanceof ConstantValue ) )
throw new CompileError ( sizeVal . toString ( ) + " is not constant or is not defined " , new StatementSource ( ctx ) ) ;
varDeclPop ( ) ;
arraySpec = new ArraySpec ( ( ConstantValue ) sizeVal ) ;
} else {
arraySpec = new ArraySpec ( ) ;
}
final SymbolType elementDeclType = varDecl . getEffectiveType ( ) ;
SymbolType arrayDeclType = new SymbolTypePointer ( elementDeclType , arraySpec , false , false ) ;
varDecl . setVarDeclType ( arrayDeclType ) ;
return null ;
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeNameDeclaratorArray ( KickCParser . TypeNameDeclaratorArrayContext ctx ) {
this . visit ( ctx . typeNameDeclarator ( ) ) ;
// Handle array type declaration by updating the declared type and the array spec
ArraySpec arraySpec ;
if ( ctx . expr ( ) ! = null ) {
varDeclPush ( ) ;
RValue sizeVal = ( RValue ) visit ( ctx . expr ( ) ) ;
if ( ! ( sizeVal instanceof ConstantValue ) )
throw new CompileError ( sizeVal . toString ( ) + " is not constant or is not defined " , new StatementSource ( ctx ) ) ;
varDeclPop ( ) ;
arraySpec = new ArraySpec ( ( ConstantValue ) sizeVal ) ;
} else {
arraySpec = new ArraySpec ( ) ;
}
final SymbolType elementDeclType = varDecl . getEffectiveType ( ) ;
SymbolType arrayDeclType = new SymbolTypePointer ( elementDeclType , arraySpec , false , false ) ;
varDecl . setVarDeclType ( arrayDeclType ) ;
return varDecl . getEffectiveType ( ) ;
}
2021-05-02 12:28:03 +00:00
@Override
public Object visitDeclaratorPar ( KickCParser . DeclaratorParContext ctx ) {
this . visit ( ctx . declarator ( ) ) ;
return null ;
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeNameDeclaratorPar ( KickCParser . TypeNameDeclaratorParContext ctx ) {
this . visit ( ctx . typeNameDeclarator ( ) ) ;
return varDecl . getEffectiveType ( ) ;
}
2021-05-02 12:28:03 +00:00
@Override
public Object visitDeclaratorName ( KickCParser . DeclaratorNameContext ctx ) {
varDecl . setVarName ( ctx . getText ( ) ) ;
return null ;
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeNameDeclaratorName ( KickCParser . TypeNameDeclaratorNameContext ctx ) {
return null ;
}
2021-05-02 12:28:03 +00:00
@Override
public Object visitDeclaratorProcedure ( KickCParser . DeclaratorProcedureContext ctx ) {
2021-05-11 21:11:51 +00:00
List < ParameterDecl > parameters = new ArrayList < > ( ) ;
List < SymbolType > paramTypes = new ArrayList < > ( ) ;
if ( ctx . parameterListDecl ( ) ! = null )
for ( KickCParser . ParameterDeclContext parameterDeclContext : ctx . parameterListDecl ( ) . parameterDecl ( ) ) {
varDeclPush ( ) ;
ParameterDecl paramDecl = ( ParameterDecl ) this . visit ( parameterDeclContext ) ;
// Handle parameter list with "VOID"
2021-08-08 11:47:48 +00:00
if ( SymbolType . VOID . equals ( paramDecl . type ) & & ctx . parameterListDecl ( ) . parameterDecl ( ) . size ( ) = = 1 )
2021-05-11 21:11:51 +00:00
; // Ignore the void parameter
else {
paramTypes . add ( paramDecl . type ) ;
parameters . add ( paramDecl ) ;
}
varDeclPop ( ) ;
}
2021-05-02 12:28:03 +00:00
SymbolType returnType = varDecl . getEffectiveType ( ) ;
2021-05-11 21:11:51 +00:00
varDecl . setVarDeclType ( new SymbolTypeProcedure ( returnType , paramTypes ) ) ;
varDecl . setParameters ( parameters ) ;
visit ( ctx . declarator ( ) ) ;
2021-05-02 12:28:03 +00:00
return null ;
}
2021-05-13 08:26:33 +00:00
@Override
public Object visitTypeNameDeclaratorProcedure ( KickCParser . TypeNameDeclaratorProcedureContext ctx ) {
List < ParameterDecl > parameters = new ArrayList < > ( ) ;
List < SymbolType > paramTypes = new ArrayList < > ( ) ;
if ( ctx . parameterListDecl ( ) ! = null )
for ( KickCParser . ParameterDeclContext parameterDeclContext : ctx . parameterListDecl ( ) . parameterDecl ( ) ) {
varDeclPush ( ) ;
ParameterDecl paramDecl = ( ParameterDecl ) this . visit ( parameterDeclContext ) ;
// Handle parameter list with "VOID"
2021-08-08 11:47:48 +00:00
if ( SymbolType . VOID . equals ( paramDecl . type ) & & ctx . parameterListDecl ( ) . parameterDecl ( ) . size ( ) = = 1 )
2021-05-13 08:26:33 +00:00
; // Ignore the void parameter
else {
paramTypes . add ( paramDecl . type ) ;
parameters . add ( paramDecl ) ;
}
varDeclPop ( ) ;
}
SymbolType returnType = varDecl . getEffectiveType ( ) ;
varDecl . setVarDeclType ( new SymbolTypeProcedure ( returnType , paramTypes ) ) ;
varDecl . setParameters ( parameters ) ;
visit ( ctx . typeNameDeclarator ( ) ) ;
return varDecl . getEffectiveType ( ) ;
}
2019-06-12 23:53:33 +00:00
@Override
2020-03-29 22:12:03 +00:00
public Object visitTypeNamedRef ( KickCParser . TypeNamedRefContext ctx ) {
2019-06-14 22:32:09 +00:00
Scope typeDefScope = program . getScope ( ) . getTypeDefScope ( ) ;
2020-04-09 20:17:33 +00:00
Variable typeDefVariable = typeDefScope . getLocalVar ( ctx . getText ( ) ) ;
2019-08-03 12:08:48 +00:00
if ( typeDefVariable ! = null ) {
2020-03-30 06:35:22 +00:00
varDecl . setDeclType ( typeDefVariable . getType ( ) ) ;
2020-03-29 22:12:03 +00:00
return null ;
2019-06-14 22:32:09 +00:00
}
2019-08-03 12:08:48 +00:00
throw new CompileError ( " Unknown type " + ctx . getText ( ) , new StatementSource ( ctx ) ) ;
2019-06-12 23:53:33 +00:00
}
2019-06-14 22:32:09 +00:00
@Override
public Object visitTypeDef ( KickCParser . TypeDefContext ctx ) {
Scope typedefScope = program . getScope ( ) . getTypeDefScope ( ) ;
scopeStack . push ( typedefScope ) ;
2020-03-27 21:23:04 +00:00
this . visit ( ctx . declType ( ) ) ;
2021-05-02 19:11:03 +00:00
this . visit ( ctx . declarator ( ) ) ;
String typedefName = varDecl . getVarName ( ) ;
2024-04-26 09:40:14 +00:00
VariableBuilder varBuilder = new VariableBuilder ( typedefName , getCurrentScope ( ) , false , false , varDecl . getEffectiveType ( ) , varDecl . getDeclDirectives ( ) , currentSegmentData , program . getTargetPlatform ( ) . getVariableBuilderConfig ( ) , program ) ;
2021-05-01 11:08:08 +00:00
varBuilder . build ( ) ;
2024-04-19 13:55:24 +00:00
if ( typedefName ! = null ) {
// #820/50 - Define the name of structures, based on the typedef name, if any.
// For structures, the generation of CDecl has a hidden and conceptual problem ...
// Structures with an empty name get the structure name $n allocated by the compiler.
// And this name can vary depending on the structure declaration order etc.
// Also, the structure name struct $0 etc is not C syntax compatible.
// So, in order to avoid conflicts, we use the typedef name if given.
// We could also have given the struct name the typedef name appended with _s,
// but that idea seemed to be way too complicated to implement.
// Because the generated struct definition name $n gets assigned during parsing
// before the typedef name is parsed.
// It is complicated to replace the structure definition name once it is defined and allocated.
// So the idea is that the typedef name gets stored as part of the structure type definition,
// and used in CDecl when needed.
if ( varDecl . declType instanceof SymbolTypeStruct ) {
ProgramScope programScope = program . getScope ( ) ;
StructDefinition structDef = ( ( SymbolTypeStruct ) varDecl . declType ) . getStructDefinition ( programScope ) ;
if ( structDef . getLocalName ( ) . startsWith ( " $ " ) ) {
varDecl . declType . setTypeDefName ( typedefName ) ;
//StructDefinition structDefinition = ((SymbolTypeStruct) varDecl.declType).replaceStructDefinition(programScope, typedefName);
StructDefinition structDefinition = ( ( SymbolTypeStruct ) varDecl . declType ) . getStructDefinition ( programScope ) ;
structDefinition . setTypeDefStruct ( ( SymbolTypeStruct ) varDecl . declType ) ;
//((SymbolTypeStruct) varDecl.declType).setStructName(typedefName);
}
}
}
2019-06-14 22:32:09 +00:00
scopeStack . pop ( ) ;
2020-03-27 20:08:18 +00:00
varDecl . exitType ( ) ;
2019-06-14 22:32:09 +00:00
return null ;
}
2019-08-29 20:52:58 +00:00
/ * *
* RValues that have not yet been output as part of a statement .
2019-08-25 19:37:10 +00:00
* The expression visitor methods updates this so that the surrounding
* statement can make sure to output any rValue that has not been output .
* Null if we are not currently monitoring this .
2019-08-29 20:52:58 +00:00
* /
2019-10-17 09:13:46 +00:00
private Set < RValue > exprNotConsumed = null ;
2019-08-25 19:37:10 +00:00
/ * *
* Begins monitoring list of expressions not consumed .
* /
2019-10-17 09:13:46 +00:00
private void beginNotConsumedTracking ( ) {
2019-08-25 19:37:10 +00:00
exprNotConsumed = new LinkedHashSet < > ( ) ;
}
/ * *
* Ends monitoring list of expressions not consumed .
* /
2019-10-17 09:13:46 +00:00
private void endNotConsumedTracking ( ) {
2019-08-25 19:37:10 +00:00
exprNotConsumed = null ;
}
/ * *
* Consumes an RValue by outputting it as part of a statement .
* This helps ensure that all expression RValues are output in statements
2019-08-29 20:52:58 +00:00
*
2019-08-25 19:37:10 +00:00
* @param rValue The RValue being consume
* /
2019-10-17 09:13:46 +00:00
private void consumeExpr ( RValue rValue ) {
2019-08-29 20:52:58 +00:00
if ( exprNotConsumed ! = null )
2019-08-25 20:21:56 +00:00
exprNotConsumed . remove ( rValue ) ;
2019-08-25 19:37:10 +00:00
}
/ * *
* Marks an expression that has been produced which has not been output as part of a statement .
* When the RValue is output in a statement it will be consumed using { @link # consumeExpr ( RValue ) } .
2019-08-29 20:52:58 +00:00
*
2019-08-25 19:37:10 +00:00
* @param rValue The RValue that has been produced but not consumed
* /
2019-10-17 09:13:46 +00:00
private void addExprToConsume ( RValue rValue ) {
2019-08-29 20:52:58 +00:00
if ( exprNotConsumed ! = null )
2019-08-25 19:37:10 +00:00
exprNotConsumed . add ( rValue ) ;
}
2019-08-29 20:52:58 +00:00
/ * *
* Examines whether an RValue is in the list of non - consumed RValues .
2019-08-25 19:37:10 +00:00
*
* @return true if the RValue is in the list
* /
2019-10-17 09:13:46 +00:00
private boolean notConsumed ( RValue rValue ) {
2019-08-25 19:37:10 +00:00
return exprNotConsumed . contains ( rValue ) ;
}
2017-12-26 16:24:30 +00:00
@Override
public Object visitExprAssignment ( KickCParser . ExprAssignmentContext ctx ) {
2018-07-08 21:58:43 +00:00
Object val = visit ( ctx . expr ( 0 ) ) ;
2019-11-03 19:11:06 +00:00
if ( val instanceof ConstantRef ) {
2023-04-06 20:46:28 +00:00
throw new CompileError ( " const variable may not be modified " + val , new StatementSource ( ctx ) ) ;
2019-11-03 19:11:06 +00:00
}
2023-04-06 20:46:28 +00:00
if ( ! ( val instanceof LValue lValue ) ) {
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Illegal assignment Lvalue " + val . toString ( ) , new StatementSource ( ctx ) ) ;
2019-03-08 05:33:59 +00:00
}
2017-12-26 16:24:30 +00:00
if ( lValue instanceof VariableRef & & ( ( VariableRef ) lValue ) . isIntermediate ( ) ) {
// Encountered an intermediate variable. This must be turned into a proper LValue later. Put it into a marker to signify that
lValue = new LvalueIntermediate ( ( VariableRef ) lValue ) ;
}
RValue rValue = ( RValue ) this . visit ( ctx . expr ( 1 ) ) ;
2019-12-24 10:05:32 +00:00
Statement stmt = new StatementAssignment ( lValue , rValue , false , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmt ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( lValue ) ;
consumeExpr ( rValue ) ;
2017-12-26 16:24:30 +00:00
return lValue ;
}
2018-04-28 10:06:01 +00:00
@Override
public Object visitExprAssignmentCompound ( KickCParser . ExprAssignmentCompoundContext ctx ) {
// Assignment (rValue/lValue)
2018-08-02 20:57:20 +00:00
Object value = visit ( ctx . expr ( 0 ) ) ;
2023-04-06 20:46:28 +00:00
if ( ! ( value instanceof LValue lValue ) ) {
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Illegal assignment Lvalue " + value . toString ( ) , new StatementSource ( ctx ) ) ;
2018-08-02 20:57:20 +00:00
}
2018-04-28 10:06:01 +00:00
if ( lValue instanceof VariableRef & & ( ( VariableRef ) lValue ) . isIntermediate ( ) ) {
// Encountered an intermediate variable. This must be turned into a proper LValue later. Put it into a marker to signify that
lValue = new LvalueIntermediate ( ( VariableRef ) lValue ) ;
}
RValue rValue = ( RValue ) this . visit ( ctx . expr ( 1 ) ) ;
2020-09-27 22:01:06 +00:00
if ( rValue instanceof LValue ) {
rValue = copyLValue ( ( LValue ) rValue ) ;
}
2018-04-28 10:06:01 +00:00
// Binary Operator
String op = ( ( TerminalNode ) ctx . getChild ( 1 ) ) . getSymbol ( ) . getText ( ) ;
Operator operator = Operators . getBinaryCompound ( op ) ;
// Assignment with operator
2019-04-19 06:42:42 +00:00
LValue rValue1 = copyLValue ( lValue ) ;
2019-12-24 10:05:32 +00:00
Statement stmt = new StatementAssignment ( lValue , rValue1 , operator , rValue , false , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmt ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( lValue ) ;
consumeExpr ( rValue ) ;
2018-04-28 10:06:01 +00:00
return lValue ;
}
2021-06-11 19:41:01 +00:00
public static LValue copyLValue ( LValue lValue ) {
2019-04-19 06:42:42 +00:00
if ( lValue instanceof VariableRef ) {
return new VariableRef ( ( ( VariableRef ) lValue ) . getFullName ( ) ) ;
} else if ( lValue instanceof LvalueIntermediate ) {
return new LvalueIntermediate ( ( VariableRef ) copyLValue ( ( ( LvalueIntermediate ) lValue ) . getVariable ( ) ) ) ;
} else if ( lValue instanceof PointerDereferenceSimple ) {
2019-08-25 19:37:10 +00:00
RValue pointer = ( ( PointerDereferenceSimple ) lValue ) . getPointer ( ) ;
if ( pointer instanceof LValue ) {
pointer = copyLValue ( ( LValue ) pointer ) ;
}
return new PointerDereferenceSimple ( pointer ) ;
2019-04-19 06:42:42 +00:00
} else if ( lValue instanceof PointerDereferenceIndexed ) {
2019-08-25 19:37:10 +00:00
RValue pointer = ( ( PointerDereferenceIndexed ) lValue ) . getPointer ( ) ;
if ( pointer instanceof LValue ) {
pointer = copyLValue ( ( LValue ) pointer ) ;
}
RValue index = ( ( PointerDereferenceIndexed ) lValue ) . getIndex ( ) ;
if ( index instanceof LValue ) {
index = copyLValue ( ( LValue ) index ) ;
}
return new PointerDereferenceIndexed ( pointer , index ) ;
2019-06-15 17:17:55 +00:00
} else if ( lValue instanceof StructMemberRef ) {
return new StructMemberRef ( copyLValue ( ( LValue ) ( ( StructMemberRef ) lValue ) . getStruct ( ) ) , ( ( StructMemberRef ) lValue ) . getMemberName ( ) ) ;
2019-08-25 19:37:10 +00:00
} else if ( lValue instanceof ForwardVariableRef ) {
return new ForwardVariableRef ( ( ( ForwardVariableRef ) lValue ) . getName ( ) ) ;
2019-04-19 06:42:42 +00:00
} else {
2019-05-30 21:50:26 +00:00
throw new CompileError ( " Unknown LValue type " + lValue ) ;
2019-04-19 06:42:42 +00:00
}
}
2019-05-27 06:39:59 +00:00
@Override
public Object visitExprDot ( KickCParser . ExprDotContext ctx ) {
RValue structExpr = ( RValue ) visit ( ctx . expr ( ) ) ;
String name = ctx . NAME ( ) . getText ( ) ;
2019-08-25 19:37:10 +00:00
StructMemberRef structMemberRef = new StructMemberRef ( structExpr , name ) ;
addExprToConsume ( structMemberRef ) ;
return structMemberRef ;
2019-05-27 06:39:59 +00:00
}
2019-06-10 15:02:17 +00:00
@Override
public Object visitExprArrow ( KickCParser . ExprArrowContext ctx ) {
RValue structPtrExpr = ( RValue ) visit ( ctx . expr ( ) ) ;
String name = ctx . NAME ( ) . getText ( ) ;
2019-08-25 19:37:10 +00:00
StructMemberRef structMemberRef = new StructMemberRef ( new PointerDereferenceSimple ( structPtrExpr ) , name ) ;
addExprToConsume ( structMemberRef ) ;
return structMemberRef ;
2019-06-10 15:02:17 +00:00
}
2017-05-23 06:51:39 +00:00
@Override
public RValue visitExprCast ( KickCParser . ExprCastContext ctx ) {
2017-11-24 22:25:16 +00:00
RValue child = ( RValue ) this . visit ( ctx . expr ( ) ) ;
2021-05-13 08:26:33 +00:00
SymbolType castType = ( SymbolType ) this . visit ( ctx . typeName ( ) ) ;
2018-03-06 18:50:26 +00:00
Operator operator = Operators . getCastUnary ( castType ) ;
2019-11-03 23:39:09 +00:00
if ( child instanceof ConstantValue ) {
consumeExpr ( child ) ;
return new ConstantCastValue ( castType , ( ConstantValue ) child ) ;
2019-11-18 21:02:29 +00:00
} else {
2020-04-19 09:05:54 +00:00
return new CastValue ( castType , child ) ;
2019-11-03 23:39:09 +00:00
}
2017-05-23 06:51:39 +00:00
}
2019-04-17 07:34:10 +00:00
@Override
2019-04-17 19:27:32 +00:00
public Object visitExprSizeOf ( KickCParser . ExprSizeOfContext ctx ) {
2021-08-08 23:48:50 +00:00
// sizeof(expression) - add a unary expression to be resolved later
RValue child = ( RValue ) this . visit ( ctx . expr ( ) ) ;
Variable tmpVar = addIntermediateVar ( ) ;
SymbolVariableRef tmpVarRef = tmpVar . getRef ( ) ;
Statement stmt = new StatementAssignment ( ( LValue ) tmpVarRef , Operators . SIZEOF , child , true , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
addStatement ( stmt ) ;
consumeExpr ( child ) ;
return tmpVarRef ;
}
@Override
public Object visitExprSizeOfType ( KickCParser . ExprSizeOfTypeContext ctx ) {
// sizeof(type) - add directly
SymbolType type = ( SymbolType ) this . visit ( ctx . typeName ( ) ) ;
return SizeOfConstants . getSizeOfConstantVar ( program . getScope ( ) , type ) ;
2019-04-22 09:50:48 +00:00
}
@Override
public Object visitExprTypeId ( KickCParser . ExprTypeIdContext ctx ) {
2021-08-08 23:48:50 +00:00
// typeid(expression) - add a unary expression to be resolved later
RValue child = ( RValue ) this . visit ( ctx . expr ( ) ) ;
Variable tmpVar = addIntermediateVar ( ) ;
SymbolVariableRef tmpVarRef = tmpVar . getRef ( ) ;
Statement stmt = new StatementAssignment ( ( LValue ) tmpVarRef , Operators . TYPEID , child , true , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
addStatement ( stmt ) ;
consumeExpr ( child ) ;
return tmpVarRef ;
}
@Override
public Object visitExprTypeIdType ( KickCParser . ExprTypeIdTypeContext ctx ) {
// typeid(type) - add directly
SymbolType type = ( SymbolType ) this . visit ( ctx . typeName ( ) ) ;
return OperatorTypeId . getTypeIdConstantVar ( program . getScope ( ) , type ) ;
2019-04-17 07:34:10 +00:00
}
2017-05-23 06:51:39 +00:00
@Override
public Object visitExprCall ( KickCParser . ExprCallContext ctx ) {
2017-07-12 22:13:26 +00:00
List < RValue > parameters ;
KickCParser . ParameterListContext parameterList = ctx . parameterList ( ) ;
2017-12-27 22:53:51 +00:00
if ( parameterList ! = null ) {
2017-07-13 23:59:04 +00:00
parameters = ( List < RValue > ) this . visit ( parameterList ) ;
2017-07-12 22:13:26 +00:00
} else {
parameters = new ArrayList < > ( ) ;
}
2019-04-04 15:44:14 +00:00
2021-08-08 11:47:48 +00:00
for ( RValue parameter : parameters ) {
consumeExpr ( parameter ) ;
}
2019-04-04 15:44:14 +00:00
if ( ctx . expr ( ) instanceof KickCParser . ExprIdContext ) {
2021-08-08 11:47:48 +00:00
String procedureName = ctx . expr ( ) . getText ( ) ;
2021-07-01 22:21:08 +00:00
// Handle the special BYTE0/1/2/3/WORD0/1/MAKEWORD/MAKEDWORD calls
2021-05-02 12:28:03 +00:00
if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_BYTE0_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . BYTE0 , parameters . get ( 0 ) ) ;
2021-05-02 12:28:03 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_BYTE1_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . BYTE1 , parameters . get ( 0 ) ) ;
2021-05-02 12:28:03 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_BYTE2_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . BYTE2 , parameters . get ( 0 ) ) ;
2021-05-02 12:28:03 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_BYTE3_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . BYTE3 , parameters . get ( 0 ) ) ;
2021-05-02 12:28:03 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_WORD0_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . WORD0 , parameters . get ( 0 ) ) ;
2021-05-02 12:28:03 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_WORD1_NAME . equals ( procedureName ) & & parameters . size ( ) = = 1 ) {
2021-08-08 11:47:48 +00:00
return addExprUnary ( ctx , Operators . WORD1 , parameters . get ( 0 ) ) ;
2021-07-01 22:21:08 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKEWORD . equals ( procedureName ) & & parameters . size ( ) = = 2 ) {
2021-08-08 11:47:48 +00:00
return addExprBinary ( ctx , parameters . get ( 0 ) , Operators . WORD , parameters . get ( 1 ) ) ;
2021-07-01 22:21:08 +00:00
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKELONG . equals ( procedureName ) & & parameters . size ( ) = = 2 ) {
2021-08-08 11:47:48 +00:00
return addExprBinary ( ctx , parameters . get ( 0 ) , Operators . DWORD , parameters . get ( 1 ) ) ;
} else if ( Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKELONG4 . equals ( procedureName ) ) {
// Handle the intrinsic MAKELONG4()
if ( program . getScope ( ) . getGlobalSymbol ( Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKELONG4 ) = = null ) {
// Add the intrinsic MAKEWORD4() to the global scope
final Procedure makeword4 = new Procedure (
Pass1ByteXIntrinsicRewrite . INTRINSIC_MAKELONG4 ,
new SymbolTypeProcedure ( SymbolType . DWORD , Arrays . asList ( new SymbolType [ ] { SymbolType . BYTE , SymbolType . BYTE , SymbolType . BYTE , SymbolType . BYTE } ) ) ,
program . getScope ( ) ,
Scope . SEGMENT_CODE_DEFAULT , Scope . SEGMENT_DATA_DEFAULT ,
2023-04-23 20:04:23 +00:00
Procedure . CallingConvention . INTRINSIC_CALL , Bank . COMMON ) ;
2021-08-08 11:47:48 +00:00
makeword4 . setDeclaredIntrinsic ( true ) ;
final Variable hihi = new Variable ( " hihi " , Variable . Kind . PHI_MASTER , SymbolType . BYTE , makeword4 , Variable . MemoryArea . ZEROPAGE_MEMORY , Scope . SEGMENT_DATA_DEFAULT , null ) ;
makeword4 . add ( hihi ) ;
final Variable hilo = new Variable ( " hilo " , Variable . Kind . PHI_MASTER , SymbolType . BYTE , makeword4 , Variable . MemoryArea . ZEROPAGE_MEMORY , Scope . SEGMENT_DATA_DEFAULT , null ) ;
makeword4 . add ( hilo ) ;
final Variable lohi = new Variable ( " lohi " , Variable . Kind . PHI_MASTER , SymbolType . BYTE , makeword4 , Variable . MemoryArea . ZEROPAGE_MEMORY , Scope . SEGMENT_DATA_DEFAULT , null ) ;
makeword4 . add ( lohi ) ;
final Variable lolo = new Variable ( " lolo " , Variable . Kind . PHI_MASTER , SymbolType . BYTE , makeword4 , Variable . MemoryArea . ZEROPAGE_MEMORY , Scope . SEGMENT_DATA_DEFAULT , null ) ;
makeword4 . add ( lolo ) ;
makeword4 . setParameters ( Arrays . asList ( hihi , hilo , lohi , lolo ) ) ;
program . getScope ( ) . add ( makeword4 ) ;
}
// Add the call
LValue result = ( LValue ) addIntermediateVar ( ) . getRef ( ) ;
addStatement ( new StatementCall ( result , procedureName , parameters , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
return result ;
2021-04-05 00:02:30 +00:00
}
2021-08-08 11:47:48 +00:00
}
// Examine if the procedureName references a variable
RValue procedurePointer = ( RValue ) this . visit ( ctx . expr ( ) ) ;
if ( procedurePointer instanceof ProcedureRef ) {
// A normal named call
LValue result = ( LValue ) addIntermediateVar ( ) . getRef ( ) ;
String procedureName = ( ( ProcedureRef ) procedurePointer ) . getLocalName ( ) ;
addStatement ( new StatementCall ( result , procedureName , parameters , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
return result ;
} else if ( procedurePointer instanceof ForwardVariableRef ) {
// TODO: Remove the need for forward references!
// Assume this is a named call to a yet undeclared function.
LValue result = ( LValue ) addIntermediateVar ( ) . getRef ( ) ;
String procedureName = ( ( ForwardVariableRef ) procedurePointer ) . getName ( ) ;
addStatement ( new StatementCall ( result , procedureName , parameters , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
return result ;
2019-04-04 15:44:14 +00:00
} else {
2021-08-08 11:47:48 +00:00
LValue result = ( LValue ) addIntermediateVar ( ) . getRef ( ) ;
addStatement ( new StatementCallPointer ( result , procedurePointer , parameters , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( procedurePointer ) ;
2021-08-01 21:27:59 +00:00
Label afterCallLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
addStatement ( new StatementLabel ( afterCallLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
2021-08-08 11:47:48 +00:00
return result ;
2019-08-25 19:37:10 +00:00
}
2017-06-05 14:01:50 +00:00
}
@Override
public List < RValue > visitParameterList ( KickCParser . ParameterListContext ctx ) {
List < RValue > parameters = new ArrayList < > ( ) ;
2017-12-27 22:53:51 +00:00
for ( KickCParser . ExprContext exprContext : ctx . expr ( ) ) {
2017-06-05 14:01:50 +00:00
RValue param = ( RValue ) this . visit ( exprContext ) ;
parameters . add ( param ) ;
}
return parameters ;
2017-05-23 06:51:39 +00:00
}
@Override
2017-05-28 06:58:22 +00:00
public RValue visitExprArray ( KickCParser . ExprArrayContext ctx ) {
2019-04-15 12:03:50 +00:00
RValue array = ( RValue ) visit ( ctx . expr ( ) ) ;
RValue index = ( RValue ) visit ( ctx . commaExpr ( ) ) ;
2019-08-25 19:37:10 +00:00
PointerDereferenceIndexed pointerDereferenceIndexed = new PointerDereferenceIndexed ( array , index ) ;
addExprToConsume ( pointerDereferenceIndexed ) ;
return pointerDereferenceIndexed ;
2017-05-23 06:51:39 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
public RValue visitExprNumber ( KickCParser . ExprNumberContext ctx ) {
2019-08-25 12:38:44 +00:00
try {
return NumberParser . parseIntegerLiteral ( ctx . getText ( ) ) ;
} catch ( NumberFormatException e ) {
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
}
2017-05-07 18:32:30 +00:00
}
2019-06-02 15:25:04 +00:00
/** The current string encoding used if no explicit encoding is specified. */
2020-08-08 00:25:11 +00:00
private StringEncoding currentEncoding ;
2019-06-02 15:25:04 +00:00
2017-05-07 18:32:30 +00:00
@Override
public RValue visitExprString ( KickCParser . ExprStringContext ctx ) {
2019-08-18 14:43:15 +00:00
StringBuilder stringValue = new StringBuilder ( ) ;
2019-08-07 17:36:19 +00:00
String subText ;
2019-06-02 15:25:04 +00:00
String lastSuffix = " " ;
2020-04-10 09:24:20 +00:00
StringEncoding encoding = null ;
2019-05-24 18:57:43 +00:00
for ( TerminalNode stringNode : ctx . STRING ( ) ) {
subText = stringNode . getText ( ) ;
2019-06-02 15:25:04 +00:00
String suffix = subText . substring ( subText . lastIndexOf ( '"' ) + 1 ) ;
2020-04-10 20:02:30 +00:00
StringEncoding suffixEncoding = StringEncoding . fromSuffix ( suffix , currentEncoding ) ;
2019-08-08 11:10:23 +00:00
if ( suffixEncoding ! = null ) {
2019-08-03 12:08:48 +00:00
if ( encoding ! = null & & ! encoding . equals ( suffixEncoding ) ) {
2019-06-02 15:25:04 +00:00
throw new CompileError ( " Cannot mix encodings in concatenated strings " + ctx . getText ( ) , new StatementSource ( ctx ) ) ;
2019-08-03 12:08:48 +00:00
}
encoding = suffixEncoding ;
2019-05-24 18:57:43 +00:00
}
2019-06-02 15:25:04 +00:00
lastSuffix = suffix ;
2019-08-18 14:43:15 +00:00
stringValue . append ( subText , 1 , subText . lastIndexOf ( '"' ) ) ;
2019-05-24 18:57:43 +00:00
}
2019-08-07 17:36:19 +00:00
boolean zeroTerminated = ! lastSuffix . contains ( " z " ) ;
2019-08-18 14:43:15 +00:00
try {
2020-04-10 09:24:20 +00:00
return new ConstantString ( encoding . escapeToAscii ( stringValue . toString ( ) ) , encoding , zeroTerminated ) ;
2019-08-18 14:43:15 +00:00
} catch ( CompileError e ) {
// Rethrow - adding statement context!
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
}
2017-05-07 18:32:30 +00:00
}
@Override
public RValue visitExprBool ( KickCParser . ExprBoolContext ctx ) {
String bool = ctx . getText ( ) ;
return new ConstantBool ( Boolean . valueOf ( bool ) ) ;
}
2017-10-19 08:21:32 +00:00
@Override
public Object visitExprChar ( KickCParser . ExprCharContext ctx ) {
2019-08-18 14:43:15 +00:00
try {
String charText = ctx . getText ( ) ;
charText = charText . substring ( 1 , charText . length ( ) - 1 ) ;
2020-04-10 09:24:20 +00:00
char constChar = currentEncoding . escapeToAscii ( charText ) . charAt ( 0 ) ;
2019-08-18 14:43:15 +00:00
return new ConstantChar ( constChar , currentEncoding ) ;
2019-08-21 15:33:11 +00:00
} catch ( CompileError e ) {
2019-08-18 14:43:15 +00:00
// Rethrow adding source location
throw new CompileError ( e . getMessage ( ) , new StatementSource ( ctx ) ) ;
}
2017-10-19 08:21:32 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
public RValue visitExprBinary ( KickCParser . ExprBinaryContext ctx ) {
2017-05-23 06:51:39 +00:00
RValue left = ( RValue ) this . visit ( ctx . expr ( 0 ) ) ;
RValue right = ( RValue ) this . visit ( ctx . expr ( 1 ) ) ;
2017-06-05 14:01:50 +00:00
String op = ( ( TerminalNode ) ctx . getChild ( 1 ) ) . getSymbol ( ) . getText ( ) ;
2018-03-06 18:50:26 +00:00
Operator operator = Operators . getBinary ( op ) ;
2021-07-01 22:21:08 +00:00
return addExprBinary ( ctx , left , operator , right ) ;
}
private RValue addExprBinary ( ParserRuleContext ctx , RValue left , Operator operator , RValue right ) {
2019-06-18 23:23:27 +00:00
if ( left instanceof ConstantValue & & right instanceof ConstantValue ) {
return new ConstantBinary ( ( ConstantValue ) left , ( OperatorBinary ) operator , ( ConstantValue ) right ) ;
2019-08-03 12:08:48 +00:00
} else {
2020-06-21 08:23:04 +00:00
Variable tmpVar = addIntermediateVar ( ) ;
2019-11-01 17:24:09 +00:00
SymbolVariableRef tmpVarRef = tmpVar . getRef ( ) ;
2019-12-24 10:05:32 +00:00
Statement stmt = new StatementAssignment ( ( LValue ) tmpVarRef , left , operator , right , true , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmt ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( left ) ;
consumeExpr ( right ) ;
2019-06-18 22:39:15 +00:00
return tmpVarRef ;
2019-06-18 23:23:27 +00:00
}
2017-05-07 18:32:30 +00:00
}
2017-12-26 16:24:30 +00:00
@Override
public Object visitExprPtr ( KickCParser . ExprPtrContext ctx ) {
RValue child = ( RValue ) this . visit ( ctx . expr ( ) ) ;
2019-08-25 19:37:10 +00:00
PointerDereferenceSimple pointerDereferenceSimple = new PointerDereferenceSimple ( child ) ;
addExprToConsume ( pointerDereferenceSimple ) ;
return pointerDereferenceSimple ;
2017-12-26 16:24:30 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
public RValue visitExprUnary ( KickCParser . ExprUnaryContext ctx ) {
2017-05-23 06:51:39 +00:00
RValue child = ( RValue ) this . visit ( ctx . expr ( ) ) ;
2017-06-05 14:01:50 +00:00
String op = ( ( TerminalNode ) ctx . getChild ( 0 ) ) . getSymbol ( ) . getText ( ) ;
2018-03-06 18:50:26 +00:00
Operator operator = Operators . getUnary ( op ) ;
2021-04-05 00:02:30 +00:00
return addExprUnary ( ctx , operator , child ) ;
}
private RValue addExprUnary ( ParserRuleContext ctx , Operator operator , RValue child ) {
2019-05-18 10:23:13 +00:00
// Special handling of negative literal number
2020-04-30 21:42:45 +00:00
if ( operator . equals ( Operators . ADDRESS_OF ) ) {
ConstantValue constantAddressOf = constantifyAddressOf ( child , new StatementSource ( ctx ) ) ;
if ( constantAddressOf ! = null )
return constantAddressOf ;
}
if ( operator . equals ( Operators . NEG ) & & child instanceof ConstantInteger ) {
2019-05-18 10:23:13 +00:00
return new ConstantInteger ( - ( ( ConstantInteger ) child ) . getInteger ( ) , ( ( ConstantInteger ) child ) . getType ( ) ) ;
2019-06-18 23:23:27 +00:00
} else if ( child instanceof ConstantValue ) {
return new ConstantUnary ( ( OperatorUnary ) operator , ( ConstantValue ) child ) ;
2019-05-30 21:50:26 +00:00
} else {
2020-06-21 08:23:04 +00:00
Variable tmpVar = addIntermediateVar ( ) ;
2019-11-01 17:24:09 +00:00
SymbolVariableRef tmpVarRef = tmpVar . getRef ( ) ;
2019-12-24 10:05:32 +00:00
Statement stmt = new StatementAssignment ( ( LValue ) tmpVarRef , operator , child , true , new StatementSource ( ctx ) , ensureUnusedComments ( getCommentsSymbol ( ctx ) ) ) ;
2020-06-15 20:23:06 +00:00
addStatement ( stmt ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( child ) ;
2019-05-18 10:23:13 +00:00
return tmpVarRef ;
}
2017-05-07 18:32:30 +00:00
}
2020-04-30 21:42:45 +00:00
/ * *
* Try to constantify an RValue that is affected by the address - of operator .
2020-06-15 20:23:06 +00:00
*
2020-04-30 21:42:45 +00:00
* @param child The sub expression of the address - of operator
* @param source The statement source ( used for errors )
* @return The constant value of the pointer , if it can be constantified . Null otherwise .
* /
private ConstantValue constantifyAddressOf ( RValue child , StatementSource source ) {
if ( child instanceof SymbolRef ) {
return new ConstantSymbolPointer ( ( SymbolRef ) child ) ;
} else if ( child instanceof PointerDereferenceIndexed & & ( ( PointerDereferenceIndexed ) child ) . getPointer ( ) instanceof ConstantValue & & ( ( PointerDereferenceIndexed ) child ) . getIndex ( ) instanceof ConstantValue ) {
PointerDereferenceIndexed pointerDeref = ( PointerDereferenceIndexed ) child ;
return new ConstantBinary ( ( ConstantValue ) pointerDeref . getPointer ( ) , Operators . PLUS , ( ConstantValue ) pointerDeref . getIndex ( ) ) ;
} else if ( child instanceof StructMemberRef & & ( ( StructMemberRef ) child ) . getStruct ( ) instanceof PointerDereferenceSimple & & ( ( PointerDereferenceSimple ) ( ( StructMemberRef ) child ) . getStruct ( ) ) . getPointer ( ) instanceof ConstantValue ) {
final StructMemberRef structMemberRef = ( StructMemberRef ) child ;
final ConstantValue structPointer = ( ConstantValue ) ( ( PointerDereferenceSimple ) structMemberRef . getStruct ( ) ) . getPointer ( ) ;
final SymbolTypeStruct structType = ( SymbolTypeStruct ) SymbolTypeInference . inferType ( program . getScope ( ) , structMemberRef . getStruct ( ) ) ;
StructDefinition structDefinition = structType . getStructDefinition ( program . getScope ( ) ) ;
final Variable member = structDefinition . getMember ( structMemberRef . getMemberName ( ) ) ;
if ( member = = null ) {
2021-08-10 15:48:55 +00:00
throw new CompileError ( " Unknown struct member " + structMemberRef . getMemberName ( ) + " in struct " + structType . toCDecl ( ) , source ) ;
2020-04-30 21:42:45 +00:00
}
final ConstantRef memberOffset = SizeOfConstants . getStructMemberOffsetConstant ( program . getScope ( ) , structDefinition , structMemberRef . getMemberName ( ) ) ;
return new ConstantCastValue ( new SymbolTypePointer ( member . getType ( ) ) , new ConstantBinary ( new ConstantCastValue ( new SymbolTypePointer ( SymbolType . BYTE ) , structPointer ) , Operators . PLUS , memberOffset ) ) ;
} else if ( child instanceof StructMemberRef & & ( ( StructMemberRef ) child ) . getStruct ( ) instanceof SymbolRef ) {
final StructMemberRef structMemberRef = ( StructMemberRef ) child ;
final ConstantValue structPointer = new ConstantSymbolPointer ( ( SymbolRef ) structMemberRef . getStruct ( ) ) ;
final SymbolTypeStruct structType = ( SymbolTypeStruct ) SymbolTypeInference . inferType ( program . getScope ( ) , structMemberRef . getStruct ( ) ) ;
StructDefinition structDefinition = structType . getStructDefinition ( program . getScope ( ) ) ;
final Variable member = structDefinition . getMember ( structMemberRef . getMemberName ( ) ) ;
if ( member = = null ) {
2021-08-10 15:48:55 +00:00
throw new CompileError ( " Unknown struct member " + structMemberRef . getMemberName ( ) + " in struct " + structType . toCDecl ( ) , source ) ;
2020-04-30 21:42:45 +00:00
}
final ConstantRef memberOffset = SizeOfConstants . getStructMemberOffsetConstant ( program . getScope ( ) , structDefinition , structMemberRef . getMemberName ( ) ) ;
return new ConstantCastValue ( new SymbolTypePointer ( member . getType ( ) ) , new ConstantBinary ( new ConstantCastValue ( new SymbolTypePointer ( SymbolType . BYTE ) , structPointer ) , Operators . PLUS , memberOffset ) ) ;
}
return null ;
}
2019-04-07 15:59:44 +00:00
@Override
public RValue visitExprTernary ( KickCParser . ExprTernaryContext ctx ) {
RValue condValue = ( RValue ) this . visit ( ctx . expr ( 0 ) ) ;
Label trueLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label falseLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
Label endJumpLabel = getCurrentScope ( ) . addLabelIntermediate ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementConditionalJump ( condValue , trueLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
addStatement ( new StatementLabel ( falseLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
2019-04-07 15:59:44 +00:00
RValue falseValue = ( RValue ) this . visit ( ctx . expr ( 2 ) ) ;
2020-06-21 08:23:04 +00:00
SymbolVariableRef falseVar = addIntermediateVar ( ) . getRef ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementAssignment ( ( LValue ) falseVar , falseValue , true , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
LabelRef falseExitLabel = getCurrentProcedureCompilation ( ) . getStatementSequence ( ) . getCurrentBlockLabel ( ) ;
addStatement ( new StatementJump ( endJumpLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
addStatement ( new StatementLabel ( trueLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
2019-04-07 15:59:44 +00:00
RValue trueValue = ( RValue ) this . visit ( ctx . expr ( 1 ) ) ;
2020-06-21 08:23:04 +00:00
SymbolVariableRef trueVar = addIntermediateVar ( ) . getRef ( ) ;
2020-06-15 20:23:06 +00:00
addStatement ( new StatementAssignment ( ( LValue ) trueVar , trueValue , true , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
LabelRef trueExitLabel = getCurrentProcedureCompilation ( ) . getStatementSequence ( ) . getCurrentBlockLabel ( ) ;
addStatement ( new StatementLabel ( endJumpLabel . getRef ( ) , new StatementSource ( ctx ) , Comment . NO_COMMENTS ) ) ;
2019-04-07 15:59:44 +00:00
StatementPhiBlock phiBlock = new StatementPhiBlock ( Comment . NO_COMMENTS ) ;
phiBlock . setSource ( new StatementSource ( ctx ) ) ;
2020-06-21 08:23:04 +00:00
SymbolVariableRef finalVar = addIntermediateVar ( ) . getRef ( ) ;
2019-11-01 17:24:09 +00:00
StatementPhiBlock . PhiVariable phiVariable = phiBlock . addPhiVariable ( ( VariableRef ) finalVar ) ;
2019-04-25 05:58:17 +00:00
phiVariable . setrValue ( trueExitLabel , trueVar ) ;
phiVariable . setrValue ( falseExitLabel , falseVar ) ;
2020-06-15 20:23:06 +00:00
addStatement ( phiBlock ) ;
2019-08-25 19:37:10 +00:00
consumeExpr ( condValue ) ;
consumeExpr ( falseValue ) ;
consumeExpr ( trueValue ) ;
2019-04-07 15:59:44 +00:00
return finalVar ;
}
2019-04-15 12:03:50 +00:00
@Override
public Object visitCommaNone ( KickCParser . CommaNoneContext ctx ) {
return this . visit ( ctx . expr ( ) ) ;
}
@Override
public Object visitCommaSimple ( KickCParser . CommaSimpleContext ctx ) {
this . visit ( ctx . commaExpr ( ) ) ;
return this . visit ( ctx . expr ( ) ) ;
}
2017-07-14 10:13:16 +00:00
@Override
public Object visitExprPreMod ( KickCParser . ExprPreModContext ctx ) {
2019-04-15 21:13:15 +00:00
return this . visit ( ctx . expr ( ) ) ;
2017-07-14 10:13:16 +00:00
}
2017-07-13 21:40:17 +00:00
@Override
public Object visitExprPostMod ( KickCParser . ExprPostModContext ctx ) {
2019-04-15 21:13:15 +00:00
return this . visit ( ctx . expr ( ) ) ;
2017-07-13 21:40:17 +00:00
}
2017-05-07 18:32:30 +00:00
@Override
public RValue visitExprPar ( KickCParser . ExprParContext ctx ) {
2019-04-15 12:03:50 +00:00
return ( RValue ) this . visit ( ctx . commaExpr ( ) ) ;
2017-05-07 18:32:30 +00:00
}
@Override
public RValue visitExprId ( KickCParser . ExprIdContext ctx ) {
2020-04-09 20:17:33 +00:00
Symbol symbol = getCurrentScope ( ) . findSymbol ( ctx . NAME ( ) . getText ( ) ) ;
2019-11-01 19:46:10 +00:00
if ( symbol instanceof Variable ) {
Variable variable = ( Variable ) symbol ;
2018-04-22 09:10:47 +00:00
return variable . getRef ( ) ;
2018-08-05 11:33:23 +00:00
} else if ( symbol instanceof Procedure ) {
Procedure procedure = ( Procedure ) symbol ;
2018-08-05 15:28:02 +00:00
return procedure . getRef ( ) ;
} else if ( symbol = = null ) {
2018-04-22 09:10:47 +00:00
// Either forward reference or a non-existing variable. Create a forward reference for later resolving.
return new ForwardVariableRef ( ctx . NAME ( ) . getText ( ) ) ;
2017-07-31 23:33:42 +00:00
}
2020-11-08 19:45:22 +00:00
throw new CompileError ( " Unhandled symbol " + symbol . toString ( program ) ) ;
2017-05-07 18:32:30 +00:00
}
2019-02-17 10:03:55 +00:00
/ * *
* Find all comments preceding the passed context .
* Group the comments into blocks each time an empty line ( double newline ) is encountered
*
* @param ctx The parse context to examine
* @return The comment blocks preceding the context
* /
private List < List < Comment > > getCommentBlocks ( ParserRuleContext ctx ) {
List < List < Comment > > commentBlocks = new ArrayList < > ( ) ;
List < Comment > comments = new ArrayList < > ( ) ;
2020-04-11 11:41:10 +00:00
BufferedTokenStream preprocessedTokenStream = cParser . getPreprocessedTokenStream ( ) ;
2020-04-05 12:45:32 +00:00
final int startTokenIndex = ctx . start . getTokenIndex ( ) ;
2020-04-08 21:40:27 +00:00
if ( startTokenIndex < 0 )
2020-04-05 12:45:32 +00:00
return commentBlocks ;
2020-04-11 11:41:10 +00:00
List < Token > hiddenTokens = preprocessedTokenStream . getHiddenTokensToLeft ( startTokenIndex ) ;
2019-02-17 10:03:55 +00:00
if ( hiddenTokens ! = null ) {
for ( Token hiddenToken : hiddenTokens ) {
2020-04-05 07:32:10 +00:00
if ( hiddenToken . getChannel ( ) = = CParser . CHANNEL_WHITESPACE ) {
2019-02-17 10:03:55 +00:00
String text = hiddenToken . getText ( ) ;
long newlineCount = text . chars ( ) . filter ( ch - > ch = = '\n' ) . count ( ) ;
2019-03-14 23:02:33 +00:00
if ( newlineCount > 1 & & comments . size ( ) > 0 ) {
2019-02-17 10:03:55 +00:00
// Create new comment block
commentBlocks . add ( comments ) ;
comments = new ArrayList < > ( ) ;
}
2020-04-05 07:32:10 +00:00
} else if ( hiddenToken . getChannel ( ) = = CParser . CHANNEL_COMMENTS ) {
2019-02-17 23:12:29 +00:00
boolean isBlock = false ;
2019-02-17 10:03:55 +00:00
String text = hiddenToken . getText ( ) ;
if ( text . startsWith ( " // " ) ) {
text = text . substring ( 2 ) ;
}
2019-02-17 23:12:29 +00:00
if ( text . startsWith ( " /* " ) ) {
2019-03-14 23:02:33 +00:00
text = text . substring ( 2 , text . length ( ) - 2 ) ;
2019-02-17 23:12:29 +00:00
isBlock = true ;
}
2019-02-17 10:03:55 +00:00
Comment comment = new Comment ( text ) ;
2019-02-17 23:12:29 +00:00
comment . setBlock ( isBlock ) ;
2019-02-17 10:03:55 +00:00
comment . setTokenIndex ( hiddenToken . getTokenIndex ( ) ) ;
2019-03-14 23:02:33 +00:00
comments . add ( comment ) ;
2019-02-17 10:03:55 +00:00
}
}
}
2019-03-14 23:02:33 +00:00
if ( comments . size ( ) > 0 ) {
2019-02-17 10:03:55 +00:00
commentBlocks . add ( comments ) ;
}
return commentBlocks ;
}
/** Set containing the token index of all comment blocks that have already been used. */
2021-05-01 11:08:08 +00:00
private final HashSet < Integer > usedCommentTokenIndices = new HashSet < > ( ) ;
2019-02-17 10:03:55 +00:00
/ * *
* Ensures that the comments have not already been " used " in another context .
*
* @param candidate The comments to examine
* @return The comments if they are unused . An empty comment if they had already been used .
* /
private List < Comment > ensureUnusedComments ( List < Comment > candidate ) {
2019-03-14 23:02:33 +00:00
if ( candidate . size ( ) = = 0 ) {
2019-02-17 14:50:42 +00:00
return candidate ;
}
2019-02-17 10:03:55 +00:00
int tokenIndex = candidate . get ( 0 ) . getTokenIndex ( ) ;
if ( usedCommentTokenIndices . contains ( tokenIndex ) ) {
// Comment was already used - Return an empty list
return new ArrayList < > ( ) ;
} else {
// Comment unused - Mark as used and return it
usedCommentTokenIndices . add ( tokenIndex ) ;
return candidate ;
}
}
/ * *
* Find the first comments preceding the passed context ( search from start until meeting a double newline ) .
* Only returns comments if they have not already been " used " by another call .
*
* @param ctx The parse context to examine
* @return The first comments preceding the context
* /
private List < Comment > getCommentsFile ( ParserRuleContext ctx ) {
List < List < Comment > > commentBlocks = getCommentBlocks ( ctx ) ;
2019-03-14 23:02:33 +00:00
if ( commentBlocks . size ( ) = = 0 ) {
2019-02-17 10:03:55 +00:00
return new ArrayList < > ( ) ;
}
2019-02-17 14:50:42 +00:00
return commentBlocks . get ( 0 ) ;
2019-02-17 10:03:55 +00:00
}
/ * *
* Find comments immediately preceding the passed context ( search from end until meeting a double newline )
* Only returns comments if they have not already been " used " by another call .
*
* @param ctx The parse context to examine
* @return The comments immediately preceding the context
* /
private List < Comment > getCommentsSymbol ( ParserRuleContext ctx ) {
List < List < Comment > > commentBlocks = getCommentBlocks ( ctx ) ;
2019-03-14 23:02:33 +00:00
if ( commentBlocks . size ( ) = = 0 ) {
2019-02-17 10:03:55 +00:00
return new ArrayList < > ( ) ;
}
2019-02-17 14:50:42 +00:00
return commentBlocks . get ( commentBlocks . size ( ) - 1 ) ;
2019-02-17 10:03:55 +00:00
}
2019-04-19 23:44:54 +00:00
2019-08-25 09:00:49 +00:00
private static class PrePostModifierHandler extends KickCParserBaseVisitor < Void > {
2017-07-13 23:59:04 +00:00
2021-05-01 11:08:08 +00:00
private final List < PrePostModifier > postMods ;
private final List < PrePostModifier > preMods ;
private final Pass0GenerateStatementSequence mainParser ;
2017-07-13 23:59:04 +00:00
2019-10-17 09:13:46 +00:00
PrePostModifierHandler ( Pass0GenerateStatementSequence mainParser ) {
2017-07-13 23:59:04 +00:00
this . mainParser = mainParser ;
2017-07-14 10:13:16 +00:00
preMods = new ArrayList < > ( ) ;
2017-07-13 23:59:04 +00:00
postMods = new ArrayList < > ( ) ;
}
2019-10-17 09:13:46 +00:00
static void addPostModifiers ( Pass0GenerateStatementSequence parser , ParserRuleContext ctx , StatementSource statementSource ) {
2017-07-14 10:13:16 +00:00
PrePostModifierHandler prePostModifierHandler = new PrePostModifierHandler ( parser ) ;
prePostModifierHandler . visit ( ctx ) ;
List < PrePostModifier > modifiers = prePostModifierHandler . getPostMods ( ) ;
2019-07-08 14:43:09 +00:00
addModifierStatements ( parser , modifiers , statementSource ) ;
2017-07-14 10:13:16 +00:00
}
2019-10-17 09:13:46 +00:00
static void addPreModifiers ( Pass0GenerateStatementSequence parser , ParserRuleContext ctx , StatementSource statementSource ) {
2017-07-14 10:13:16 +00:00
PrePostModifierHandler modifierHandler = new PrePostModifierHandler ( parser ) ;
modifierHandler . visit ( ctx ) ;
List < PrePostModifier > modifiers = modifierHandler . getPreMods ( ) ;
2019-07-08 14:43:09 +00:00
addModifierStatements ( parser , modifiers , statementSource ) ;
2017-07-14 10:13:16 +00:00
}
2019-11-22 22:41:01 +00:00
static boolean hasPrePostModifiers ( Pass0GenerateStatementSequence parser , ParserRuleContext ctx , StatementSource statementSource ) {
PrePostModifierHandler modifierHandler = new PrePostModifierHandler ( parser ) ;
modifierHandler . visit ( ctx ) ;
2020-07-06 15:40:15 +00:00
return ( modifierHandler . getPreMods ( ) . size ( ) > 0 ) | | ( modifierHandler . getPostMods ( ) . size ( ) > 0 ) ;
}
static boolean hasPostModifiers ( Pass0GenerateStatementSequence parser , ParserRuleContext ctx , StatementSource statementSource ) {
PrePostModifierHandler modifierHandler = new PrePostModifierHandler ( parser ) ;
modifierHandler . visit ( ctx ) ;
return modifierHandler . getPostMods ( ) . size ( ) > 0 ;
2019-11-22 22:41:01 +00:00
}
2017-07-14 10:13:16 +00:00
private static void addModifierStatements (
2017-12-29 17:20:11 +00:00
Pass0GenerateStatementSequence parser ,
2018-05-05 19:34:24 +00:00
List < PrePostModifier > modifiers ,
StatementSource source ) {
2017-12-27 22:53:51 +00:00
for ( PrePostModifier mod : modifiers ) {
2020-10-11 13:56:17 +00:00
if ( mod . child instanceof ConstantValue ) {
2019-11-18 21:02:29 +00:00
throw new CompileError ( " Constants can not be modified " + mod . child . toString ( ) , source ) ;
2019-11-03 19:11:06 +00:00
}
2019-12-24 10:05:32 +00:00
Statement stmt = new StatementAssignment ( ( LValue ) mod . child , mod . operator , copyLValue ( ( LValue ) mod . child ) , false , source , Comment . NO_COMMENTS ) ;
2020-06-15 20:23:06 +00:00
parser . addStatement ( stmt ) ;
2019-08-25 19:37:10 +00:00
parser . consumeExpr ( mod . child ) ;
2018-08-22 22:24:32 +00:00
if ( parser . program . getLog ( ) . isVerboseParse ( ) ) {
2018-11-11 20:51:36 +00:00
parser . program . getLog ( ) . append ( " Adding pre/post-modifier " + stmt . toString ( parser . program , false ) ) ;
2018-08-22 22:24:32 +00:00
}
2017-07-13 23:59:04 +00:00
}
}
2019-02-17 14:50:42 +00:00
List < PrePostModifier > getPreMods ( ) {
2018-01-01 20:25:11 +00:00
return preMods ;
}
2019-02-17 14:50:42 +00:00
List < PrePostModifier > getPostMods ( ) {
2018-01-01 20:25:11 +00:00
return postMods ;
}
2017-07-13 23:59:04 +00:00
@Override
public Void visitExprPostMod ( KickCParser . ExprPostModContext ctx ) {
2019-07-09 09:40:56 +00:00
// First handle the ++/-- modifier
2017-07-13 23:59:04 +00:00
RValue child = ( RValue ) mainParser . visit ( ctx . expr ( ) ) ;
String op = ( ( TerminalNode ) ctx . getChild ( 1 ) ) . getSymbol ( ) . getText ( ) ;
2018-03-06 18:50:26 +00:00
Operator operator = Operators . getUnary ( op ) ;
2017-07-14 10:13:16 +00:00
PrePostModifier modifier = new PrePostModifier ( child , operator ) ;
postMods . add ( modifier ) ;
2019-07-09 09:40:56 +00:00
// First visit sub-expressions in case they have ++/-- themselves
this . visit ( ctx . expr ( ) ) ;
2017-07-14 10:13:16 +00:00
return null ;
}
@Override
public Void visitExprPreMod ( KickCParser . ExprPreModContext ctx ) {
2019-07-09 09:40:56 +00:00
// First handle the ++/-- modifier
2017-07-14 10:13:16 +00:00
RValue child = ( RValue ) mainParser . visit ( ctx . expr ( ) ) ;
String op = ( ( TerminalNode ) ctx . getChild ( 0 ) ) . getSymbol ( ) . getText ( ) ;
2018-03-06 18:50:26 +00:00
Operator operator = Operators . getUnary ( op ) ;
2017-07-14 10:13:16 +00:00
PrePostModifier modifier = new PrePostModifier ( child , operator ) ;
preMods . add ( modifier ) ;
2019-07-09 09:40:56 +00:00
// Then visit sub-expressions in case they have ++/--
this . visit ( ctx . expr ( ) ) ;
2017-07-13 23:59:04 +00:00
return null ;
}
2017-07-14 10:13:16 +00:00
private static class PrePostModifier {
2019-02-17 14:50:42 +00:00
RValue child ;
2017-07-13 23:59:04 +00:00
public Operator operator ;
2019-02-17 14:50:42 +00:00
PrePostModifier ( RValue child , Operator operator ) {
2017-07-13 23:59:04 +00:00
this . child = child ;
this . operator = operator ;
}
}
}
2017-05-07 18:32:30 +00:00
}