wudsn-ide/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntax.java
2019-01-12 23:07:17 +01:00

937 lines
34 KiB
Java

/**
* Copyright (C) 2009 - 2019 <a href="https://www.wudsn.com" target="_top">Peter Dell</a>
*
* This file is part of WUDSN IDE.
*
* WUDSN IDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* WUDSN IDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>.
*/
package com.wudsn.ide.asm.compiler.syntax;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import com.wudsn.ide.asm.CPU;
import com.wudsn.ide.asm.compiler.CompilerRegistry;
import com.wudsn.ide.base.common.StringUtility;
/**
* Container for a set of directives, legal, illegal and pseudo opcodes and
* their properties.
*
* @author Peter Dell
* @author Daniel Mitte
*
*/
public final class CompilerSyntax {
public static final char NO_CHARACTER = (char) 0;
private static final class XMLHandler extends DefaultHandler {
// Completion proposal auto activation characters, possibly empty array
// of characters.
String completionProposalAutoActivationCharactersText;
// Single line delimiters, list of strings separated by spaces.
String singleLineCommentDelimitersText;
// Single line delimiters, list of strings separated by spaces.
String multipleLinesCommentDelimitersText;
// Single line delimiters, non empty array of characters.
String stringDelimiterCharactersText;
// The (possibly empty) string of character pairs which define the begin
// and end of a named or unnamed (folding) block.
String blockDefinitionCharactersText;
// The boolean value indicating if identifiers are case sensitive or
// not.
boolean identifiersCaseSensitive;
// The string containing all allowed identifier start characters.
String identifierStartCharactersText;
// The string containing all allowed identifier start characters.
String identifierPartCharactersText;
// The string containing the character which separates two parts of a
// compound identifier or
// the empty string if compound identifiers are not supported.
String identifierSeparatorCharacterText;
// The string containing the character which end a label definition or
// the empty string if label definitions end at the first white space.
String labelDefinitionSuffixCharacterText;
// The string containing the character which start a macro usage or
// the empty string if macro usages are not prefixed with a character.
String macroUsagePrefixCharacterText;
// The boolean value indicating if instructions are case sensitive or
// not.
boolean instructionsCaseSensitive;
// Default file extension to be added if it is missing for source
// include directives.
String sourceIncludeDefaultExtension;
List<Instruction> instructionsList;
public XMLHandler() {
instructionsList = new ArrayList<Instruction>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName == null) {
throw new IllegalArgumentException("Parameter 'qName' must not be null.");
}
if (attributes == null) {
throw new IllegalArgumentException("Parameter 'attributes' must not be null.");
}
if (qName.equals("instructionset")) {
// Completion proposal auto activation characters
completionProposalAutoActivationCharactersText = attributes
.getValue("completionProposalAutoActivationCharacters");
if (completionProposalAutoActivationCharactersText == null) {
throw new SAXException("No completionProposalAutoActivationCharacters specified.");
}
// Single lines comments.
singleLineCommentDelimitersText = attributes.getValue("singleLineCommentDelimiters");
if (singleLineCommentDelimitersText == null) {
throw new SAXException("No singleLineCommentDelimiters specified.");
}
if (StringUtility.isEmpty(singleLineCommentDelimitersText)) {
throw new SAXException("Attribute singleLineCommentDelimiterst must not be empty.");
}
// Multiple lines comments.
multipleLinesCommentDelimitersText = attributes.getValue("multipleLinesCommentDelimiters");
if (multipleLinesCommentDelimitersText == null) {
throw new SAXException("No multipleLinesCommentDelimiters specified.");
}
// Strings.
stringDelimiterCharactersText = attributes.getValue("stringDelimiterCharacters");
if (stringDelimiterCharactersText == null) {
throw new SAXException("No stringDelimiterCharacters specified.");
}
if (StringUtility.isEmpty(stringDelimiterCharactersText)) {
throw new SAXException("Attribute stringDelimiterCharacters must not be empty.");
}
// Block definition characters
blockDefinitionCharactersText = attributes.getValue("blockDefinitionCharacters");
if (blockDefinitionCharactersText == null) {
throw new SAXException("No blockDefinitionCharacters specified.");
}
if (blockDefinitionCharactersText.length() % 2 != 0) {
throw new SAXException("Attribute blockDefinitionCharacters must and even number of characters.");
}
// Identifiers: Case sensitive.
String value = attributes.getValue("identifiersCaseSensitive");
if (value == null) {
throw new SAXException("No identifiersCaseSensitive specified.");
}
if (!value.equals("true") && !value.equals("false")) {
throw new SAXException("Attribute identifiersCaseSensitive must be \"true\" or \"false\".");
}
identifiersCaseSensitive = Boolean.parseBoolean(value);
// Identifiers: Start characters.
identifierStartCharactersText = attributes.getValue("identifierStartCharacters");
if (identifierStartCharactersText == null) {
throw new SAXException("No identifierStartCharacters specified.");
}
if (StringUtility.isEmpty(identifierStartCharactersText)) {
throw new SAXException("Attribute identifierStartCharacters must not be empty.");
}
// Identifiers: Start characters.
identifierPartCharactersText = attributes.getValue("identifierPartCharacters");
if (identifierPartCharactersText == null) {
throw new SAXException("No identifierPartCharacters specified.");
}
if (StringUtility.isEmpty(identifierPartCharactersText)) {
throw new SAXException("Attribute identifierPartCharacters must not be empty.");
}
// Identifiers: Separator character.
identifierSeparatorCharacterText = attributes.getValue("identifierSeparatorCharacter");
if (identifierSeparatorCharacterText == null) {
throw new SAXException("No identifierSeparatorCharacter specified.");
}
if (identifierSeparatorCharacterText.length() > 1) {
throw new SAXException(
"Attribute identifierSeparatorCharacter must not contain more than 1 characters.");
}
// Identifiers: Label definition suffix character.
labelDefinitionSuffixCharacterText = attributes.getValue("labelDefinitionSuffixCharacter");
if (labelDefinitionSuffixCharacterText == null) {
throw new SAXException("No labelDefinitionSuffixCharacter specified.");
}
if (labelDefinitionSuffixCharacterText.length() > 1) {
throw new SAXException(
"Attribute labelDefinitionSuffixCharacter must not contain more than 1 characters.");
}
// Identifiers: Macro usage prefix character.
macroUsagePrefixCharacterText = attributes.getValue("macroUsagePrefixCharacter");
if (macroUsagePrefixCharacterText == null) {
throw new SAXException("No macroUsagePrefixCharacter specified.");
}
if (macroUsagePrefixCharacterText.length() > 1) {
throw new SAXException(
"Attribute macroUsagePrefixCharacter must not contain more than 1 characters.");
}
// Instructions case sensitive.
value = attributes.getValue("instructionsCaseSensitive");
if (value == null) {
throw new SAXException("No instructionsCaseSensitive specified.");
}
if (!value.equals("true") && !value.equals("false")) {
throw new SAXException("Attribute instructionsCaseSensitive must be \"true\" or \"false\".");
}
instructionsCaseSensitive = Boolean.parseBoolean(value);
// Source include default extension.
sourceIncludeDefaultExtension = attributes.getValue("sourceIncludeDefaultExtension");
if (sourceIncludeDefaultExtension == null) {
throw new SAXException("No sourceIncludeDefaultExtension specified.");
}
} else if (qName.equals("opcodes")) {
// Nothing to do.
} else {
// Begin parsing of instructions.
String cpusString;
Set<CPU> cpus;
String name;
String title;
String proposal;
String typeString;
int type;
name = attributes.getValue("name");
if (name == null) {
throw new SAXException("No name specified for '" + qName + "'.");
}
cpusString = attributes.getValue("cpus");
if (cpusString == null) {
throw new SAXException("No CPUs specified for '" + name + "'.");
}
cpus = new TreeSet<CPU>();
StringTokenizer tokenizer = new StringTokenizer(cpusString, ",");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
boolean found = false;
// Wild card?
if (token.endsWith("*")) {
token = token.substring(0, token.length() - 1);
for (CPU cpu : CPU.values()) {
if (cpu.name().startsWith(token) || cpu == CPU.MOS65C02 && token.equals("MOS6502")) {
cpus.add(cpu);
found = true;
}
}
} else {
// Exact match
for (CPU cpu : CPU.values()) {
if (cpu.name().equals(token)) {
cpus.add(cpu);
found = true;
}
}
}
if (!found) {
throw new SAXException("No cpu matches the cpus '" + cpusString + "' for '" + name + "'.");
}
}
title = attributes.getValue("title");
if (title == null) {
throw new SAXException("No title specified for '" + name + "'.");
}
typeString = attributes.getValue("type");
if (typeString == null) {
typeString = "";
}
proposal = attributes.getValue("proposal");
if (qName.equals("constant")) {
// Constants always have a simple proposal
proposal = name + "_";
}
if (proposal == null) {
throw new SAXException("No proposal specified for '" + name + "'.");
}
// TODO Have constant as own instruction class
if (qName.equals("constant")) {
qName = "directive";
typeString = "DIRECTIVE";
}
if (qName.equals("directive")) {
// Default and default nesting for folding blocks and
// sections.
if (typeString.equals("DIRECTIVE")) {
type = InstructionType.DIRECTIVE;
} else if (typeString.equals("BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_FOLDING_BLOCK_DIRECTIVE")) {
type = InstructionType.BEGIN_FOLDING_BLOCK_DIRECTIVE;
} else if (typeString.equals("END_FOLDING_BLOCK_DIRECTIVE")) {
type = InstructionType.END_FOLDING_BLOCK_DIRECTIVE;
} else if (typeString.equals("END_SECTION_DIRECTIVE")) {
type = InstructionType.END_SECTION_DIRECTIVE;
}
// Begin of sections.
else if (typeString.equals("BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_LOCAL_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_LOCAL_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_PAGES_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_PAGES_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE;
} else if (typeString.equals("BEGIN_REPEAT_SECTION_DIRECTIVE")) {
type = InstructionType.BEGIN_REPEAT_SECTION_DIRECTIVE;
}
// File references.
else if (typeString.equals("SOURCE_INCLUDE_DIRECTIVE")) {
type = InstructionType.SOURCE_INCLUDE_DIRECTIVE;
} else if (typeString.equals("BINARY_INCLUDE_DIRECTIVE")) {
type = InstructionType.BINARY_INCLUDE_DIRECTIVE;
} else if (typeString.equals("BINARY_OUTPUT_DIRECTIVE")) {
type = InstructionType.BINARY_OUTPUT_DIRECTIVE;
} else if (typeString.equals("")) {
throw new SAXException("No directive type specified for directive '" + name + "'.");
} else {
throw new SAXException("Unknown directive type '" + typeString + "' for directive '" + name
+ "'.");
}
instructionsList.add(new Directive(cpus, type, instructionsCaseSensitive, name, title, proposal));
} else if (qName.equals("opcode") || qName.equals("illegalopcode") || qName.equals("pseudoopcode")) {
if (qName.equals("opcode")) {
type = InstructionType.LEGAL_OPCODE;
} else if (qName.equals("illegalopcode")) {
type = InstructionType.ILLEGAL_OPCODE;
} else if (qName.equals("pseudoopcode")) {
type = InstructionType.PSEUDO_OPCODE;
} else {
throw new SAXException("Unknown qName '" + qName + "'.");
}
String flags = attributes.getValue("flags");
if (flags == null) {
flags = "@todo flags";
}
String modes = attributes.getValue("modes");
if (modes == null) {
modes = "todo=$00";
}
// Currently only KickAss uses case sensitive instructions
// and they are lower case. There this logic is applied here
// instead of having an additional XML attribute to control
// the lower/upper case setting for opcodes.
if (instructionsCaseSensitive) {
name = name.toLowerCase();
proposal = proposal.toLowerCase();
}
instructionsList.add(new Opcode(cpus, type, instructionsCaseSensitive, name, title, proposal, cpus
.contains(CPU.MOS65816), flags, modes));
}
}
}
}
private String compilerId;
private char[] completionProposalAutoActivationCharacters;
private List<String> singleLineCommentDelimiters;
private List<String> multipleLinesCommentDelimiters;
private List<String> stringDelimiters;
private String blockDefinitionCharacters;
private char blockDefinitionStartCharacter;
private char blockDefinitionEndCharacter;
private boolean identifiersCaseSensitive;
private String identifierStartCharacters;
private boolean identifierStartCharactersArray[];
private String identifierPartCharacters;
private boolean identifierPartCharactersArray[];
private char identifierSeparatorCharacter;
private char labelDefinitionSuffixCharacter;
private char macroUsagePrefixCharacter;
private boolean instructionsCaseSensitive;
private String sourceIncludeDefaultExtension;
private List<Instruction> instructionList;
private Map<CPU, InstructionSet> instructionSets;
/**
* Creates new instance. Called by {@link CompilerRegistry}.
*
* @param compilerId
* The compiler id, not empty and <code>null</code>.
*/
public CompilerSyntax(String compilerId) {
if (compilerId == null) {
throw new IllegalArgumentException("Parameter 'compilerId' must not be null.");
}
if (StringUtility.isEmpty(compilerId)) {
throw new IllegalArgumentException("Parameter 'compilerId' must not be empty.");
}
this.compilerId = compilerId;
}
/**
* Load XML data.
*
* @param compilerClasses
* The list of compiler classes for which the XML files shall be
* loaded, not empty and not <code>null</code>. The content of
* subsequent files will extend the content of previous files.
*/
public void loadXMLData(List<Class<?>> compilerClasses) {
if (compilerClasses == null) {
throw new IllegalArgumentException("Parameter 'compilerClasses' must not be null.");
}
if (compilerClasses.isEmpty()) {
throw new IllegalArgumentException("Parameter 'compilerClasses' must not be empty.");
}
XMLHandler xmlHandler;
xmlHandler = new XMLHandler();
for (int i = 0; i < compilerClasses.size(); i++) {
Class<?> compilerClass = compilerClasses.get(i);
if (compilerClass == null) {
throw new IllegalArgumentException("Parameter 'compilerClasses[" + i + "]' must not be null.");
}
SAXParser parser;
try {
parser = SAXParserFactory.newInstance().newSAXParser();
} catch (ParserConfigurationException ex) {
throw new RuntimeException(
"Cannot create parser for compiler class '" + compilerClass.getName() + "'.", ex);
} catch (SAXException ex) {
throw new RuntimeException(
"Cannot create parser for compiler class '" + compilerClass.getName() + "'.", ex);
}
String syntaxFileName = "/" + compilerClass.getName().replace('.', '/') + ".xml";
try {
InputStream inputStream = compilerClass.getResourceAsStream(syntaxFileName);
parser.parse(inputStream, xmlHandler);
} catch (SAXParseException ex) {
throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'. Error in line "
+ ex.getLineNumber() + ", column " + ex.getColumnNumber() + ".", ex);
} catch (SAXException ex) {
throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'.", ex);
} catch (IOException ex) {
throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'.", ex);
}
}
// Completion proposal auto activation characters
completionProposalAutoActivationCharacters = xmlHandler.completionProposalAutoActivationCharactersText
.toCharArray();
// Single line comments.
String delimiterText = xmlHandler.singleLineCommentDelimitersText;
if (delimiterText == null) {
throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is no set.");
}
StringTokenizer tokenizer = new StringTokenizer(delimiterText, " ");
singleLineCommentDelimiters = new ArrayList<String>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
singleLineCommentDelimiters.add(token);
}
singleLineCommentDelimiters = Collections.unmodifiableList(singleLineCommentDelimiters);
// Multiple lines comments.
delimiterText = xmlHandler.multipleLinesCommentDelimitersText;
if (delimiterText == null) {
throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is no set.");
}
tokenizer = new StringTokenizer(delimiterText, " ");
int tokenCount = tokenizer.countTokens();
if ((tokenCount & 1) == 1) {
throw new IllegalStateException("Attribute 'multipleLinesCommentDelimiters' has an odd number of tokens: '"
+ delimiterText + "'.");
}
multipleLinesCommentDelimiters = new ArrayList<String>(tokenCount);
while (tokenizer.hasMoreTokens()) {
multipleLinesCommentDelimiters.add(tokenizer.nextToken());
}
multipleLinesCommentDelimiters = Collections.unmodifiableList(multipleLinesCommentDelimiters);
// Strings.
delimiterText = xmlHandler.stringDelimiterCharactersText;
if (delimiterText == null) {
throw new IllegalStateException("Attribute 'stringDelimiters' is not set.");
}
stringDelimiters = new ArrayList<String>(delimiterText.length());
for (int i = 0; i < delimiterText.length(); i++) {
stringDelimiters.add(delimiterText.substring(i, i + 1));
}
stringDelimiters = Collections.unmodifiableList(stringDelimiters);
// Block definitions.
if (xmlHandler.blockDefinitionCharactersText == null) {
throw new IllegalArgumentException("Attribute 'blockDefinitionCharacters' is not set.");
}
blockDefinitionCharacters = xmlHandler.blockDefinitionCharactersText;
if (blockDefinitionCharacters.length() > 0) {
if (blockDefinitionCharacters.length() == 2) {
blockDefinitionStartCharacter = blockDefinitionCharacters.charAt(0);
blockDefinitionEndCharacter = blockDefinitionCharacters.charAt(1);
} else {
throw new IllegalArgumentException("Attribute 'blockDefinitionCharacters' has the value '"
+ blockDefinitionCharacters + "' and does not have 2 characters.");
}
} else {
blockDefinitionStartCharacter = NO_CHARACTER;
blockDefinitionEndCharacter = NO_CHARACTER;
}
// Identifiers: Case sensitive.
identifiersCaseSensitive = xmlHandler.identifiersCaseSensitive;
// Identifiers: Start characters.
if (xmlHandler.identifierStartCharactersText == null) {
throw new IllegalArgumentException("Attribute 'identifierStartCharacters' is not set.");
}
identifierStartCharacters = xmlHandler.identifierStartCharactersText;
identifierStartCharactersArray = createBooleanArray(identifierStartCharacters);
// Identifiers: Start characters.
if (xmlHandler.identifierPartCharactersText == null) {
throw new IllegalArgumentException("Attribute 'identifierPartCharacters' is not set.");
}
identifierPartCharacters = xmlHandler.identifierPartCharactersText;
identifierPartCharactersArray = createBooleanArray(identifierPartCharacters);
// Identifiers: Separator characters.
if (xmlHandler.identifierSeparatorCharacterText == null) {
throw new IllegalArgumentException("Attribute 'identifierSeparatorCharacter' is not set.");
}
if (xmlHandler.identifierSeparatorCharacterText.length() == 0) {
identifierSeparatorCharacter = NO_CHARACTER;
} else {
identifierSeparatorCharacter = xmlHandler.identifierSeparatorCharacterText.charAt(0);
}
// Identifiers: Label definition suffix character.
if (xmlHandler.labelDefinitionSuffixCharacterText == null) {
throw new IllegalArgumentException("Attribute 'labelDefinitionSuffixCharacterText' is not set.");
}
if (xmlHandler.labelDefinitionSuffixCharacterText.length() == 0) {
labelDefinitionSuffixCharacter = NO_CHARACTER;
} else {
labelDefinitionSuffixCharacter = xmlHandler.labelDefinitionSuffixCharacterText.charAt(0);
}
// Identifiers: Macro usage prefix character
if (xmlHandler.macroUsagePrefixCharacterText == null) {
throw new IllegalArgumentException("Attribute 'macroUsagePrefixCharacterText' is not set.");
}
if (xmlHandler.macroUsagePrefixCharacterText.length() == 0) {
macroUsagePrefixCharacter = NO_CHARACTER;
} else {
macroUsagePrefixCharacter = xmlHandler.macroUsagePrefixCharacterText.charAt(0);
}
// Instructions case sensitive.
instructionsCaseSensitive = xmlHandler.instructionsCaseSensitive;
// Source include default extension.
sourceIncludeDefaultExtension = xmlHandler.sourceIncludeDefaultExtension;
instructionList = Collections.unmodifiableList(xmlHandler.instructionsList);
// Create instruction set map.
instructionSets = new TreeMap<CPU, InstructionSet>();
}
static boolean[] createBooleanArray(String string) {
int length = string.length();
int max = 0;
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
if (c > max) {
max = c;
}
}
boolean[] result = new boolean[max + 1];
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
result[c] = true;
}
return result;
}
/**
* Gets the completion proposal auto activation characters.
*
* @return The array of completion proposal auto activation characters, may
* be empty, not <code>null</code>.
*/
public char[] getCompletionProposalAutoActivationCharacters() {
if (completionProposalAutoActivationCharacters == null) {
throw new IllegalStateException("Attribute 'completionProposalAutoActivationCharacters' is not set.");
}
return completionProposalAutoActivationCharacters;
}
/**
* Gets the delimiter prefixes for single line comments.
*
* @return The unmodifiable list of delimiter prefixes for single line
* comments, not empty and not <code>null</code>.
*/
public List<String> getSingleLineCommentDelimiters() {
if (singleLineCommentDelimiters == null) {
throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is not set.");
}
return singleLineCommentDelimiters;
}
/**
* Gets the delimiter prefixes for multiple lines comments.
*
* @return The unmodifiable list of delimiter prefixes for single line
* comments, not empty and not <code>null</code>. The list contains
* an even number of entries where two entries constitute the start
* sequence and the end sequence of the multiple lines rules.
*/
public List<String> getMultipleLinesCommentDelimiters() {
if (multipleLinesCommentDelimiters == null) {
throw new IllegalStateException("Attribute 'multipleLinesCommentDelimiters' is not set.");
}
return multipleLinesCommentDelimiters;
}
/**
* Gets the delimiter characters for strings.
*
* @return The unmodifiable list of delimiter characters for strings, not
* empty and not <code>null</code>.
*/
public List<String> getStringDelimiters() {
if (stringDelimiters == null) {
throw new IllegalStateException("Attribute 'stringDelimiters' is not set.");
}
return stringDelimiters;
}
/**
* Gets the (possibly empty) string of character pairs which define the
* begin and end of a named or unnamed (folding) block.
*
* @return The string of character pairs which define the begin and end of a
* named or unnamed (folding) block, may be empty, not
* <code>null</code>.
* @since 1.6.1
*/
public String getBlockDefinitionCharacters() {
return blockDefinitionCharacters;
}
/**
* Gets the block definition start character if defined.
*
* @return The block definition start character or {@link #NO_CHARACTER}.
*/
public char getBlockDefinitionStartCharacter() {
return blockDefinitionStartCharacter;
}
/**
* Gets the block definition start character if defined.
*
* @return The block definition start character or {@link #NO_CHARACTER}.
*/
public char getBlockDefinitionEndCharacter() {
return blockDefinitionEndCharacter;
}
/**
* Determines if identifiers are case sensitive.
*
* @return <code>true</code> if identifiers are case sensitive,
* <code>false</code> otherwise.
*
* @since 1.6.1
*/
public boolean areIdentifiersCaseSensitive() {
return identifiersCaseSensitive;
}
/**
* Determines if a character can be the start or part of an identifier.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be the start of an
* identifier, <code>false</code> otherwise.
*/
public boolean isIdentifierCharacter(char c) {
return isIdentifierStartCharacter(c) || isIdentifierPartCharacter(c)
|| (c == identifierSeparatorCharacter && c != NO_CHARACTER);
}
/**
* Gets the valid identifier start characters.
*
* @return The non empty string of identifier start characters, not
* <code>null</code>.
*
* @since 1.6.3
*/
public String getIdentifierStartCharacters() {
return identifierStartCharacters;
}
/**
* Determines if a character can be the start of an identifier.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be the start of an
* identifier, <code>false</code> otherwise.
*
* @since 1.6.1
*/
public boolean isIdentifierStartCharacter(char c) {
return c < identifierStartCharactersArray.length && identifierStartCharactersArray[c];
}
/**
* Gets the valid identifier part characters.
*
* @return The non empty string of identifier part characters, not
* <code>null</code>.
*
* @since 1.6.3
*/
public String getIdentifierPartCharacters() {
return identifierPartCharacters;
}
/**
* Determines if a character can be the part of an identifier.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be part of an identifier,
* <code>false</code> otherwise.
* @since 1.6.1
*/
public boolean isIdentifierPartCharacter(char c) {
return c < identifierPartCharactersArray.length && identifierPartCharactersArray[c];
}
/**
* Gets the character which separates two parts of a compound identifier.
*
* @return The character which separates two parts of a compound identifier
* or {@link #NO_CHARACTER} if compound identifiers are not
* supported.
*/
public char getIdentifierSeparatorCharacter() {
return identifierSeparatorCharacter;
}
/**
* Determines if a character can be part of an identifier and separates two
* parts of a compound identifier characters.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be part of an identifier
* and separates two parts of a compound identifier,
* <code>false</code> otherwise.
* @since 1.6.1
*/
public boolean isIdentifierSeparatorCharacter(char c) {
return (c != NO_CHARACTER) && (c == identifierSeparatorCharacter);
}
/**
* Gets the character which end a label definition, e.g. ':'.
*
* @return The character which ends a label definition or
* {@link #NO_CHARACTER} if label definitions end at the first white
* space.
*
* @since 1.6.1
*/
public char getLabelDefinitionSuffixCharacter() {
return labelDefinitionSuffixCharacter;
}
/**
* Gets the character which separates two parts of a compound identifier.
*
* @return The character which start a macro usage or {@link #NO_CHARACTER}
* if macro usages are not prefixed with a character.
*
* @since 1.6.1
*/
public char getMacroUsagePrefixCharacter() {
return macroUsagePrefixCharacter;
}
/**
* Determines if instructions are case sensitive.
*
* @return <code>true</code> if instructions are case sensitive,
* <code>false</code> otherwise.
*
* @since 1.6.1
*/
public boolean areInstructionsCaseSensitive() {
return instructionsCaseSensitive;
}
/**
* Determines if a character can be the start of a number.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be the start of a number,
* <code>false</code> otherwise.
*
* @since 1.6.1
*/
public boolean isNumberStartCharacter(char c) {
return c == '$' || c == '%' || c == '~' || (c >= '0' && c <= '9');
}
/**
* Determines if a character can be the part of a number.
*
* @param c
* The character to be checked.
* @return <code>true</code> if the character can be part of a number,
* <code>false</code> otherwise.
* @since 1.6.1
*/
public boolean isNumberPartCharacter(char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
/**
* Gets the default extension (excluding the period) to use in case source
* include file name does not end with an extension.
*
* @return Gets the default extension (excluding the period) to use in case
* source include file name does not end with an extension, may be
* empty, not <code>null</code>.
*/
public String getSourceIncludeDefaultExtension() {
if (sourceIncludeDefaultExtension == null) {
throw new IllegalStateException("Variable 'sourceIncludeDefaultExtension' not yet initialized.");
}
return sourceIncludeDefaultExtension;
}
/**
* Gets the instruction set for a CPU.
*
* @param cpu
* The CPU this which the allowed list of instructions shall be
* returned, not <code>null</code>.
* @return The set of instructions supported by the compiler for the
* specified CPU, not <code>null</code>.
*
* @since 1.6.1
*/
public InstructionSet getInstructionSet(CPU cpu) {
if (cpu == null) {
throw new IllegalArgumentException("Parameter 'cpu' must not be null.");
}
InstructionSet result;
synchronized (instructionSets) {
result = instructionSets.get(cpu);
if (result == null) {
result = new InstructionSet(this, instructionList, cpu);
instructionSets.put(cpu, result);
}
}
return result;
}
@Override
public String toString() {
return compilerId;
}
}