package dtool.parser; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import melnorme.lang.tooling.ast_actual.ASTNode; import dtool.ast.declarations.DeclarationAttrib; import dtool.ast.declarations.DeclarationAttrib.AttribBodySyntax; import dtool.ast.declarations.ImportContent; import dtool.ast.declarations.ImportSelective; import dtool.ast.definitions.DefSymbol; import dtool.ast.definitions.Module; import dtool.ast.expressions.ExpLiteralMapArray.MapArrayLiteralKeyValue; import dtool.ast.expressions.InitializerArray.ArrayInitEntry; import dtool.ast.expressions.InitializerStruct.StructInitEntry; import dtool.ast.expressions.MissingParenthesesExpression; import dtool.ast.references.NamedReference; import dtool.ast.references.RefIdentifier; import dtool.ast.statements.CommonStatementList; import dtool.parser.DeeParsingChecks.DeeParsingNodeCheck; import dtool.parser.common.LexElement; import dtool.parser.common.LexElementProducer; public class DeeParsingSourceRangeChecks extends DeeParsingNodeCheck { public static void runParsingSourceRangeChecks(ASTNode node, final String fullSource) { new DeeParsingSourceRangeChecks(fullSource, node).doCheck(); } public DeeParsingSourceRangeChecks(String source, ASTNode node) { super(source, node); } public void doCheck() { switch (nodeUnderTest.getNodeType()) { case MODULE: { Module module = (Module) nodeUnderTest; int endPos = module.getEndPos(); assertTrue(module.getStartPos() == 0 && (endPos == fullSource.length() || fullSource.charAt(endPos) == 0x00 || fullSource.charAt(endPos) == 0x1A)); return; } case MISSING_EXPRESSION: if(nodeUnderTest instanceof MissingParenthesesExpression) { SourceEquivalenceChecker.assertCheck(nodeUnderTest.toStringAsCode(), ""); } return; default: basicSourceRangeCheck(); return; } } public void basicSourceRangeCheck() { if(!canBeginWithEmptySpace(nodeUnderTest)) { LexElement firstLexElement = firstLexElementInNode(); assertTrue(firstLexElement.getFullRangeStartPos() == firstLexElement.getStartPos()); } if(nodeConsumesTrailingWhiteSpace(nodeUnderTest)) { // Check that the range contains all possible whitespace assertTrue(lexElementAfterNode(nodeUnderTest).getStartPos() == 0); } } public LexElement firstLexElementInNode() { return firstLexElementInSource(fullSource.substring(nodeUnderTest.getStartPos())); } public LexElement lexElementAfterNode(ASTNode node) { return firstLexElementInSource(fullSource.substring(node.getEndPos())); } public static LexElement firstLexElementInSource(String source) { return new LexElementProducer().produceLexElement(new DeeLexer(source)); } public static boolean canBeginWithEmptySpace(final ASTNode node) { switch (node.getNodeType()) { case DECL_LIST: case SCOPED_STATEMENT_LIST: case CSTYLE_ROOT_REF: case MISSING_EXPRESSION: case MISSING_DECLARATION: return true; case REF_MODULE: case REF_IDENTIFIER: case REF_IMPORT_SELECTION: return ((NamedReference) node).isMissingCoreReference(); case IMPORT_SELECTIVE: { ImportSelective importSelective = (ImportSelective) node; return canBeginWithEmptySpace((ASTNode) importSelective.fragment); } case IMPORT_CONTENT: { ImportContent importContent = (ImportContent) node; return canBeginWithEmptySpace(importContent.moduleRef); } case STRUCT_INIT_ENTRY: { StructInitEntry initEntry = (StructInitEntry) node; return canBeginWithEmptySpace(initEntry.member != null ? initEntry.member : (ASTNode) initEntry.value); } case ARRAY_INIT_ENTRY: { ArrayInitEntry initEntry = (ArrayInitEntry) node; return canBeginWithEmptySpace(initEntry.index != null ? initEntry.index : (ASTNode) initEntry.value); } case MAPARRAY_ENTRY: { MapArrayLiteralKeyValue mapArrayEntry = (MapArrayLiteralKeyValue) node; return canBeginWithEmptySpace(mapArrayEntry.key); } case BLOCK_STATEMENT: case BLOCK_STATEMENT_UNSCOPED: { return ((CommonStatementList) node).statements == null; } default: return false; } } public static boolean nodeConsumesTrailingWhiteSpace(final ASTNode node) { if(node instanceof DeclarationAttrib) { DeclarationAttrib declAttrib = (DeclarationAttrib) node; if(declAttrib.bodySyntax == AttribBodySyntax.COLON) { return true; } } if(node instanceof RefIdentifier) { RefIdentifier refId = (RefIdentifier) node; return refId.isMissing(); } if(node instanceof DefSymbol) { return false; } return false; } }