package dtool.parser; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import java.util.ArrayList; import dtool.ast.definitions.Symbol; import dtool.ast.references.RefIdentifier; import dtool.ast.references.Reference; import dtool.parser.common.AbstractParser; import dtool.parser.common.BaseLexElement; import melnorme.lang.tooling.ast.util.NodeVector; import melnorme.lang.tooling.ast_actual.ASTNode; import melnorme.lang.tooling.common.ParserError; import melnorme.utilbox.concurrency.OperationCancellation; public abstract class DeeParser_Common extends AbstractParser { protected abstract DeeParser thisParser(); /* ----------------------------------------------------------------- */ public DeeTokens lookAheadGrouped() { return lookAheadElement().type.getGroupingToken(); } public String idTokenToString(BaseLexElement id) { return id.isMissingElement() ? null : id.getSourceValue(); } public static boolean isCloseBracketChar(DeeTokens token) { return token == DeeTokens.CLOSE_BRACE || token == DeeTokens.CLOSE_BRACKET || token == DeeTokens.CLOSE_PARENS; } /* ----------------------- List parse helper ----------------------- */ public abstract class ElementListParseHelper<T extends ASTNode> extends ParseHelper { public NodeVector<T> members; public ElementListParseHelper() { nodeStart = -1; } public void parseList(DeeTokens tkOPEN, DeeTokens tkSEP, DeeTokens tkCLOSE) throws OperationCancellation { parseList(true, tkOPEN, tkSEP, tkCLOSE, true); } public void parseList(boolean required, DeeTokens tkOPEN, DeeTokens tkSEP, DeeTokens tkCLOSE, boolean canHaveEndingSep) throws OperationCancellation { ParseHelper parse = this; if(parse.consume(tkOPEN, !required, required) == false) { return; } setStartPosition(lastLexElement().getStartPos()); ArrayList<T> membersList = new ArrayList<T>(); boolean hasEndingSep = false; boolean requireElement = false; while(true) { if(requireElement == false && tryConsume(tkCLOSE)) break; T entry = parseElement(requireElement || lookAhead() == tkSEP); if(entry != null) { membersList.add(entry); hasEndingSep = false; } if(tryConsume(tkSEP)) { hasEndingSep = true; requireElement = !canHaveEndingSep; continue; } else { parse.consumeRequired(tkCLOSE); break; } } members = nodeListView(membersList, hasEndingSep); } protected abstract T parseElement(boolean createMissing) throws OperationCancellation; } public abstract class SimpleListParseHelper<T extends ASTNode> { public NodeVector<T> members; public NodeVector<T> parseSimpleList(DeeTokens tkSEP, boolean canBeEmpty, boolean canHaveEndingSep) throws OperationCancellation { ArrayList<T> membersList = new ArrayList<T>(); do { T entry = parseElement(!canBeEmpty || lookAhead() == tkSEP); membersList.add(entry); canBeEmpty = canHaveEndingSep; } while(tryConsume(tkSEP)); members = nodeListView(membersList); return members; } protected abstract T parseElement(boolean createMissing) throws OperationCancellation; } /* ----------------------------------------------------------------- */ public static ProtoDefSymbol defSymbol(BaseLexElement id) { // possible bug here, should be srEffectiveRange return new ProtoDefSymbol(id.getSourceValue(), id.getSourceRange(), id.getMissingError()); } public final ProtoDefSymbol parseMissingDefIdNoError() { return new ProtoDefSymbol("", srAt(getSourcePosition()), null); } public final ProtoDefSymbol parseDefId() { BaseLexElement defId = consumeExpectedContentToken(DeeTokens.IDENTIFIER); return defSymbol(defId); } public final ProtoDefSymbol nullIdToParseMissingDefId(ProtoDefSymbol defId) { if(defId == null) { return parseMissingDefIdNoError(); } return defId; } public static boolean couldHaveBeenParsedAsId(Reference ref) { return ref instanceof RefIdentifier; } public static ProtoDefSymbol convertRefIdToDef(Reference ref) { assertTrue(couldHaveBeenParsedAsId(ref)); RefIdentifier refId = (RefIdentifier) ref; ParserError error = refId.isMissing() ? getMissingIdError(refId) : null; return new ProtoDefSymbol(refId.getDenulledIdentifier(), ref.getSourceRange(), error); } protected static ParserError getMissingIdError(RefIdentifier refId) { return refId.getData().getNodeErrors().iterator().next(); } public final Symbol parseIdSymbol() throws OperationCancellation { BaseLexElement token = consumeExpectedContentToken(DeeTokens.IDENTIFIER); return createIdSymbol(token); } public final Symbol createIdSymbol(BaseLexElement token) throws OperationCancellation { return conclude(token.getMissingError(), srOf(token, new Symbol(token.getSourceValue()))); } /* ----------------------------------------------------------------- */ protected class TypeId_or_Id_RuleFragment { public Reference type = null; public ProtoDefSymbol defId = null; public void parseRuleFragment(ParseHelper parse, boolean createMissing) throws OperationCancellation { type = parse.checkResult(thisParser().parseTypeReference()); if(parse.ruleBroken) { defId = parseMissingDefIdNoError(); } else if(lookAhead() == DeeTokens.IDENTIFIER) { defId = parseDefId(); } else { // No defId so far if(couldHaveBeenParsedAsId(type)) { singleIdReparse(); } else { if(type == null && !createMissing) { return; } missingDefIdParse(); } } if(parse.nodeStart == -1) { parse.setStartPosition(type != null ? type.getStartPos() : defId.getStartPos()); } } protected void singleIdReparse() { defId = convertRefIdToDef(type); type = null; } protected void missingDefIdParse() throws OperationCancellation { defId = parseDefId(); //This will create a full missing defId, with error } } protected final class TypeId_or_Type_RuleFragment extends TypeId_or_Id_RuleFragment { @Override public void singleIdReparse() { defId = parseMissingDefIdNoError(); } @Override public void missingDefIdParse() throws OperationCancellation { if(type == null) { type = thisParser().parseMissingTypeReference(true); } defId = parseMissingDefIdNoError(); } } }