From b82a4277e1c859322b214ae44ef1af190c2594dd Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 18 Oct 2019 23:56:20 +0200 Subject: [PATCH] Moved variable directive handling to separate file. Default implementation identical to existing features. --- .../dk/camelot64/kickc/model/Directive.java | 388 ++++++++++++++++++ .../Pass0GenerateStatementSequence.java | 200 ++------- 2 files changed, 418 insertions(+), 170 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/model/Directive.java diff --git a/src/main/java/dk/camelot64/kickc/model/Directive.java b/src/main/java/dk/camelot64/kickc/model/Directive.java new file mode 100644 index 000000000..846caab3c --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/Directive.java @@ -0,0 +1,388 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.statements.StatementSource; +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeArray; +import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; +import dk.camelot64.kickc.model.values.ScopeRef; + +import java.util.*; + +/** A declaration directive. */ +public interface Directive { + + /** + * The parser directive context is used for determining which directives to apply to a variable. + * It keeps track of values for the #pragmas that control default directives for variables. + * Uses the following priority + *
    + *
  1. If the directive is present in source - use that. + *
  2. If the register keyword present in source - Look in #pragma variables_register_implies for setting (register in #pragma variables_xxx does not trigger this) + *
  3. Look in #pragma variables_scope_type for setting (scope is global/local/parameter type is integer/pointer/array) + *
  4. Look in #pragma variables_type for setting (type is integer/pointer/array) + *
  5. Look in #pragma variables_scope for setting (scope is global/local/parameter) + *
  6. Look in #pragma variables_default for setting + *
  7. Use compiler default for the setting (__notconst __notregister _notalign __notvolatile __zp __initcode) + *
+ */ + class ParserContext { + + /** The different scopes deciding directive defaults. */ + public enum DirectiveScope { + GLOBAL, LOCAL, PARAMETER, STRUCT_MEMBER; + + public static DirectiveScope getFor(SymbolVariable lValue, boolean isParameter) { + if(isParameter) { + return PARAMETER; + } + Scope scope = lValue.getScope(); + return getFor(scope); + } + + private static DirectiveScope getFor(Scope scope) { + if(ScopeRef.ROOT.equals(scope.getRef())) { + return GLOBAL; + } else if(scope instanceof Procedure) { + return LOCAL; + } else if(scope instanceof StructDefinition) { + return STRUCT_MEMBER; + } else if(scope instanceof BlockScope) { + return getFor(scope.getScope()); + } else { + throw new InternalError("Scope type not handled " + scope); + } + } + } + + /** The different types deciding directive defaults. */ + public enum DirectiveType { + INTEGER, POINTER, ARRAY, STRUCT; + + /** + * Get a directive type from a variable type. + * + * @param type The variable type + * @return The directive type + */ + public static DirectiveType getFor(SymbolType type) { + if(SymbolType.isInteger(type)) { + return INTEGER; + } else if(SymbolType.BOOLEAN.equals(type)) { + return INTEGER; + } else if(SymbolType.STRING.equals(type)) { + return ARRAY; + } else if(type instanceof SymbolTypeArray) { + return ARRAY; + } else if(type instanceof SymbolTypePointer) { + return POINTER; + } else if(type instanceof SymbolTypeStruct) { + return STRUCT; + } else { + throw new InternalError("Variable type not handled " + type); + } + } + } + + /** Combination of a scope and type deciding directive defaults. */ + public static class DirectiveScopeType { + DirectiveScope directiveScope; + DirectiveType directiveType; + + public DirectiveScopeType(DirectiveScope directiveScope, DirectiveType directiveType) { + this.directiveScope = directiveScope; + this.directiveType = directiveType; + } + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + DirectiveScopeType that = (DirectiveScopeType) o; + return directiveScope == that.directiveScope && + directiveType == that.directiveType; + } + + @Override + public int hashCode() { + return Objects.hash(directiveScope, directiveType); + } + } + + /** Directives of a specific statement. Only non-null if this is a stored copy of the context for later resolution. */ + List statementDirectives; + + /** Directives implied by the 'register' keyword. */ + List registerImpliesDirectives; + + /** Default directives for a specific scope/type combination. */ + Map> scopeTypeDirectives; + + /** Default directives for a specific scope. */ + Map> scopeDirectives; + + /** Default directives for a specific type. */ + Map> typeDirectives; + + /** Default directives. */ + List defaultDirectives; + + public ParserContext() { + this.statementDirectives = null; + // Setup default directives + this.defaultDirectives = new ArrayList<>(); + //this.defaultDirectives.add(new MemoryArea(SymbolVariable.MemoryArea.ZEROPAGE_MEMORY, null)); + //this.defaultDirectives.add(new Register(true, null)); + this.registerImpliesDirectives = new ArrayList<>(); + this.typeDirectives = new HashMap<>(); + //this.typeDirectives.put(DirectiveType.ARRAY, Arrays.asList(new MemoryArea(SymbolVariable.MemoryArea.MAIN_MEMORY, null), new Register(false, null))); + this.scopeDirectives = new HashMap<>(); + this.scopeTypeDirectives = new HashMap<>(); + } + + /** + * Applies directives to a variable. Applies directives using the correct priority. + * + * @param lValue The variable to apply directives to + * @param sourceDirectives The directives found in the source code + * @param source The source line. + * @return + */ + public void applyDirectives(SymbolVariable lValue, boolean isParameter, List sourceDirectives, StatementSource source) { + DirectiveType directiveType = DirectiveType.getFor(lValue.getType()); + DirectiveScope directiveScope = DirectiveScope.getFor(lValue, isParameter); + + Const constDirective = findDirective(Const.class, sourceDirectives, directiveScope, directiveType); + if(constDirective != null) { + lValue.setDeclaredConstant(true); + lValue.setStorageStrategy(SymbolVariable.StorageStrategy.CONSTANT); + if(!(lValue.getType() instanceof SymbolTypePointer)) + lValue.setMemoryArea(SymbolVariable.MemoryArea.MAIN_MEMORY); + } + + Volatile volatileDirective = findDirective(Volatile.class, sourceDirectives, directiveScope, directiveType); + if(volatileDirective != null) { + lValue.setDeclaredVolatile(true); + } + + Export exportDirective = findDirective(Export.class, sourceDirectives, directiveScope, directiveType); + if(exportDirective != null) { + lValue.setDeclaredExport(true); + } + + Align alignDirective = findDirective(Align.class, sourceDirectives, directiveScope, directiveType); + if(alignDirective != null) { + SymbolType type = lValue.getType(); + if(type instanceof SymbolTypeArray || type.equals(SymbolType.STRING)) { + lValue.setDeclaredAlignment(alignDirective.alignment); + } else { + throw new CompileError("Error! Cannot align variable that is not a string or an array " + lValue.toString(), source); + } + } + + MemoryArea memoryAreaDirective = findDirective(MemoryArea.class, sourceDirectives, directiveScope, directiveType); + if(memoryAreaDirective != null) { + lValue.setMemoryArea(memoryAreaDirective.memoryArea); + if(memoryAreaDirective.address != null) { + if(SymbolVariable.MemoryArea.ZEROPAGE_MEMORY.equals(memoryAreaDirective.memoryArea)) { + // Allocate to specific address + if(memoryAreaDirective.address > 255) { + throw new CompileError("Error! Address not on zeropage " + memoryAreaDirective.address, source); + } + Registers.Register register = new Registers.RegisterZpMem(memoryAreaDirective.address.intValue(), -1, true); + lValue.setDeclaredRegister(register); + } else { + lValue.setDeclaredMemoryAddress(memoryAreaDirective.address); + } + } + } + + Register registerDirective = findDirective(Register.class, sourceDirectives, directiveScope, directiveType); + if(registerDirective != null) { + if(registerDirective.isRegister) { + lValue.setDeclaredAsRegister(true); + lValue.setStorageStrategy(SymbolVariable.StorageStrategy.PHI_MASTER); + if(registerDirective.name != null) { + // Ignore register directive without parameter (all variables are placed on ZP and attempted register uplift anyways) + Registers.Register register = Registers.getRegister(registerDirective.name); + if(register == null) { + throw new CompileError("Error! Unknown register " + registerDirective.name, source); + } + lValue.setDeclaredRegister(register); + } + } else { + lValue.setDeclaredNotRegister(true); + lValue.setStorageStrategy(SymbolVariable.StorageStrategy.LOAD_STORE); + } + } + + } + + + private DirectiveClass findDirective(Class directiveClass, List sourceDirectives, DirectiveScope directiveScope, DirectiveType directiveType) { + // Look in source directives + { + DirectiveClass directive = findDirective(directiveClass, sourceDirectives); + if(directive != null) return directive; + } + + /* + // Look in register implies - if register directive is present in source + if(isRegister(sourceDirectives)) { + // Look in source directives + DirectiveClass directive = findDirective(directiveClass, registerImpliesDirectives); + if(directive != null) return directive; + } + // Look in #pragma setting for scope/type combination + { + List directives = scopeTypeDirectives.get(new DirectiveScopeType(directiveScope, directiveType)); + DirectiveClass directive = findDirective(directiveClass, directives); + if(directive != null) return directive; + } + // Look in #pragma setting for type + { + List directives = typeDirectives.get(directiveType); + DirectiveClass directive = findDirective(directiveClass, directives); + if(directive != null) return directive; + } + // Look in #pragma setting for scope + { + List directives = scopeDirectives.get(directiveScope); + DirectiveClass directive = findDirective(directiveClass, directives); + if(directive != null) return directive; + } + // Look in #pragma setting for default + { + DirectiveClass directive = findDirective(directiveClass, defaultDirectives); + if(directive != null) return directive; + } + */ + // Not found! + return null; + } + + /** + * Determine if the register directive is present in the source directives. + * + * @param sourceDirectives The source directives + * @return true if the register keyword is present + */ + private boolean isRegister(List sourceDirectives) { + Register registerDirective = findDirective(Register.class, sourceDirectives); + return registerDirective != null && registerDirective.isRegister; + } + + /** + * Look for a specific directive type in a list of directives + * + * @param directiveClass The class of the type to look for + * @param directives The list of directives to search + * @param The class of the type to look for + * @return The directive if found. null if not found. + */ + private DirectiveClass findDirective(Class directiveClass, List directives) { + if(directives == null) return null; + for(Directive directive : directives) { + if(directiveClass.isInstance(directive)) { + return (DirectiveClass) directive; + } + } + // Not found! + return null; + } + + } + + + /** Variable declared constant. */ + class Const implements Directive { + } + + /** Function with specific declared calling convention. */ + class CallingConvention implements Directive { + + public Procedure.CallingConvension callingConvension; + + public CallingConvention(Procedure.CallingConvension callingConvension) { + this.callingConvension = callingConvension; + } + + } + + /** Function declared inline. */ + class Inline implements Directive { + } + + /** Variable declared volatile. */ + class Volatile implements Directive { + } + + /** Variable declared as export. */ + class Export implements Directive { + } + + /** Function declared interrupt. */ + class Interrupt implements Directive { + public Procedure.InterruptType interruptType; + + public Interrupt(Procedure.InterruptType interruptType) { + this.interruptType = interruptType; + } + } + + /** Variable memory alignment. */ + class Align implements Directive { + + int alignment; + + public Align(int alignment) { + this.alignment = alignment; + } + + } + + /** Variable register or __notregister directive . */ + class Register implements Directive { + + /** true if the directive is a register directive. false if it is a notregister directive. */ + boolean isRegister; + + /** Name of register to use for the variable (if named) */ + public String name; + + public Register(boolean isRegister, String name) { + this.isRegister = isRegister; + this.name = name; + } + + } + + /** Variable memory area declaration. */ + class MemoryArea implements Directive { + + /** The memory area. */ + SymbolVariable.MemoryArea memoryArea; + + /** Optional hard-coded address to use for storing the variable. */ + public Long address; + + public MemoryArea(SymbolVariable.MemoryArea memoryArea, Long address) { + this.memoryArea = memoryArea; + this.address = address; + } + + } + + /** Reservation of zero-page addresses */ + class ReserveZp implements Directive { + public List reservedZp; + + public ReserveZp(List reservedZp) { + this.reservedZp = reservedZp; + } + + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 94601ff58..90a2b68aa 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -45,6 +45,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor scopeStack; /** The memory area used by default for variables. */ private SymbolVariable.MemoryArea defaultMemoryArea; + /** Context used for adding directives to variables. */ + private Directive.ParserContext directiveContext; public Pass0GenerateStatementSequence(CParser cParser, KickCParser.FileContext fileCtx, Program program) { this.cParser = cParser; @@ -53,6 +55,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(); this.defaultMemoryArea = SymbolVariable.MemoryArea.ZEROPAGE_MEMORY; + this.directiveContext = new Directive.ParserContext(); scopeStack.push(program.getScope()); } @@ -265,7 +268,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor directives = declVarDirectives; Variable param = new Variable(ctx.NAME().getText(), getCurrentScope(), type, currentDataSegment, SymbolVariable.StorageStrategy.PHI_MASTER, defaultMemoryArea); // Add directives - addDirectives(param, type, directives, new StatementSource(ctx)); + addDirectives(param, true, directives, new StatementSource(ctx)); exitDeclTypes(); return param; } @@ -620,7 +623,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor directives, StatementSource source) { - for(Directive directive : directives) { - if(directive instanceof DirectiveConst) { - lValue.setDeclaredConstant(true); - lValue.setStorageStrategy(SymbolVariable.StorageStrategy.CONSTANT); - if(!(lValue.getType() instanceof SymbolTypePointer)) - lValue.setMemoryArea(SymbolVariable.MemoryArea.MAIN_MEMORY); - } else if(directive instanceof DirectiveVolatile) { - lValue.setDeclaredVolatile(true); - } else if(directive instanceof DirectiveExport) { - lValue.setDeclaredExport(true); - } else if(directive instanceof DirectiveAlign) { - if(type instanceof SymbolTypeArray || type.equals(SymbolType.STRING)) { - lValue.setDeclaredAlignment(((DirectiveAlign) directive).alignment); - } else { - throw new CompileError("Error! Cannot align variable that is not a string or an array " + lValue.toString(program), source); - } - } else if(directive instanceof DirectiveMemoryArea) { - DirectiveMemoryArea directiveMemoryArea = (DirectiveMemoryArea) directive; - lValue.setMemoryArea(directiveMemoryArea.memoryArea); - if(directiveMemoryArea.address!=null) { - if(SymbolVariable.MemoryArea.ZEROPAGE_MEMORY.equals(directiveMemoryArea.memoryArea)) { - // Allocate to specific address - if(directiveMemoryArea.address > 255) { - throw new CompileError("Error! Address not on zeropage " + directiveMemoryArea.address, source); - } - Registers.Register register = new Registers.RegisterZpMem(directiveMemoryArea.address.intValue(), -1, true); - lValue.setDeclaredRegister(register); - } else { - lValue.setDeclaredMemoryAddress(directiveMemoryArea.address); - } - } - } else if(directive instanceof DirectiveRegister) { - DirectiveRegister directiveRegister = (DirectiveRegister) directive; - if(directiveRegister.isRegister) { - lValue.setDeclaredAsRegister(true); - lValue.setStorageStrategy(SymbolVariable.StorageStrategy.PHI_MASTER); - if(directiveRegister.name != null) { - // Ignore register directive without parameter (all variables are placed on ZP and attempted register uplift anyways) - Registers.Register register = Registers.getRegister(directiveRegister.name); - if(register == null) { - throw new CompileError("Error! Unknown register " + directiveRegister.name, source); - } - lValue.setDeclaredRegister(register); - } - } else { - lValue.setDeclaredNotRegister(true); - lValue.setStorageStrategy(SymbolVariable.StorageStrategy.LOAD_STORE); - } - } else { - throw new CompileError("Unsupported variable directive " + directive, source); - } - } + private void addDirectives(SymbolVariable lValue, boolean isParameter, List directives, StatementSource source) { + directiveContext.applyDirectives(lValue, isParameter, directives, source); } /** @@ -756,14 +708,14 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor directives, StatementSource source) { for(Directive directive : directives) { - if(directive instanceof DirectiveInline) { + if(directive instanceof Directive.Inline) { procedure.setDeclaredInline(true); - } else if(directive instanceof DirectiveCallingConvention) { - procedure.setCallingConvension(((DirectiveCallingConvention) directive).callingConvension); - } else if(directive instanceof DirectiveInterrupt) { - procedure.setInterruptType(((DirectiveInterrupt) directive).interruptType); - } else if(directive instanceof DirectiveReserveZp) { - procedure.setReservedZps(((DirectiveReserveZp) directive).reservedZp); + } else if(directive instanceof Directive.CallingConvention) { + procedure.setCallingConvension(((Directive.CallingConvention) directive).callingConvension); + } else if(directive instanceof Directive.Interrupt) { + procedure.setInterruptType(((Directive.Interrupt) directive).interruptType); + } else if(directive instanceof Directive.ReserveZp) { + procedure.setReservedZps(((Directive.ReserveZp) directive).reservedZp); } else { throw new CompileError("Unsupported function directive " + directive, source); } @@ -780,7 +732,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor directives = getDirectives(directivesCtx); for(Directive directive : directives) { StatementSource source = new StatementSource(directivesCtx.get(0)); - if(directive instanceof DirectiveInline) { + if(directive instanceof Directive.Inline) { conditional.setDeclaredUnroll(true); } else { throw new CompileError("Unsupported loop directive " + directive, source); @@ -790,12 +742,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor reservedZp; - - DirectiveReserveZp(List reservedZp) { - this.reservedZp = reservedZp; - } - - } private static class PrePostModifierHandler extends KickCParserBaseVisitor {