#include "alfe/main.h" #ifndef INCLUDED_PARSER_H #define INCLUDED_PARSER_H #include "alfe/code.h" #include "alfe/space.h" class Parser : Uncopyable { public: CodeList parseFromFile(File file) { CodeList c; parseFromFile(c, file); return c; } void parseFromString(Code code, String s) { CharacterSource source(s); do { CharacterSource s2 = source; if (s2.get() == -1) return; parseStatementOrFail(code, &source); } while (true); } private: void parseFromFile(Code code, File file) { parseFromString(code, file.contents()); } bool parseStatement(Code code, CharacterSource* source) { if (parseExpressionStatement(code, source)) return true; if (parseFunctionDefinitionStatement(code, source)) return true; if (parseAssignment(code, source)) return true; if (parseCompoundStatement(code, source)) return true; if (parseTycoDefinitionStatement(code, source)) return true; if (parseNothingStatement(source)) return true; if (parseIncrementDecrementStatement(code, source)) return true; if (parseConditionalStatement(code, source)) return true; if (parseSwitchStatement(code, source)) return true; if (parseReturnStatement(code, source)) return true; if (parseIncludeStatement(code, source)) return true; if (parseBreakOrContinueStatement(code, source)) return true; if (parseForeverStatement(code, source)) return true; if (parseWhileStatement(code, source)) return true; if (parseForStatement(code, source)) return true; if (parseLabelStatement(code, source)) return true; if (parseGotoStatement(code, source)) return true; return false; } void parseStatementOrFail(Code code, CharacterSource* source) { if (!parseStatement(code, source)) source->location().throwError("Expected statement"); } bool parseExpressionStatement(Code code, CharacterSource* source) { CharacterSource s = *source; Expression expression = Expression::parse(&s); if (!expression.valid()) return false; Span span; if (!Space::parseCharacter(&s, ';', &span)) return false; _lastSpan = span; *source = s; if (!expression.mightHaveSideEffect()) source->location().throwError("Statement has no effect"); code.insert(expression, expression.span() + span); return true; } bool parseAssignment(Code code, CharacterSource* source) { CharacterSource s = *source; Expression left = Expression::parse(&s); Location operatorLocation = s.location(); if (!left.valid()) return false; Span span; static const Operator ops[] = { OperatorAssignment(), OperatorAddAssignment(), OperatorSubtractAssignment(), OperatorMultiplyAssignment(), OperatorDivideAssignment(), OperatorModuloAssignment(), OperatorShiftLeftAssignment(), OperatorShiftRightAssignment(), OperatorBitwiseAndAssignment(), OperatorBitwiseOrAssignment(), OperatorBitwiseXorAssignment(), OperatorPowerAssignment(), Operator() }; const Operator* op; for (op = ops; op->valid(); ++op) if (Space::parseOperator(&s, op->toString(), &span)) break; if (!op->valid()) return false; *source = s; Expression right = Expression::parseOrFail(source); Space::assertCharacter(source, ';', &span); _lastSpan = span; code.insert(FunctionCallExpression::binary(*op, span, FunctionCallExpression::unary(OperatorAmpersand(), Span(), left), right), left.span() + span); return true; } List parseParameterList(CharacterSource* source) { List list; VariableDefinition parameter = VariableDefinition::parse(source); if (!parameter.valid()) return list; list.add(parameter); Span span; while (Space::parseCharacter(source, ',', &span)) { VariableDefinition parameter = VariableDefinition::parse(source); if (!parameter.valid()) source->location().throwError("Expected parameter"); list.add(parameter); } return list; } bool parseFunctionDefinitionStatement(Code code, CharacterSource* source) { CharacterSource s = *source; TycoSpecifier returnTypeSpecifier = TycoSpecifier::parse(&s); if (!returnTypeSpecifier.valid()) return false; Identifier name = Identifier::parse(&s); if (!name.valid()) return false; Span span; if (!Space::parseCharacter(&s, '(')) return false; *source = s; List parameterList = parseParameterList(source); Space::assertCharacter(source, ')'); Span span; if (Space::parseKeyword(source, "from", &span)) { Expression dll = Expression::parseOrFail(source); Space::assertCharacter(source, ';', &span); _lastSpan = span; code.insert(returnTypeSpecifier, name, parameterList, dll, returnTypeSpecifier.span() + span); return true; } CodeList body; parseStatementOrFail(body, source); code.insert(returnTypeSpecifier, name, parameterList, body, returnTypeSpecifier.span() + _lastSpan); return true; } void parseStatementSequence(Code code, CharacterSource* source) { do { if (!parseStatement(code, source)) return; } while (true); } bool parseCompoundStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseCharacter(source, '{', &span)) return false; parseStatementSequence(code, source); Space::assertCharacter(source, '}', &span); _lastSpan = span; return true; } // TycoDefinitionStatement := TycoSignifier "=" TycoSpecifier ";" bool parseTycoDefinitionStatement(Code code, CharacterSource* source) { CharacterSource s = *source; CharacterSource s2 = s; TycoSignifier tycoSignifier = TycoSignifier::parse(&s); if (!tycoSignifier.valid()) return false; if (!Space::parseCharacter(&s, '=')) return false; *source = s; TycoSpecifier tycoSpecifier = TycoSpecifier::parse(source); Span span; Space::assertCharacter(source, ';', &span); _lastSpan = span; code.insert(tycoSignifier, tycoSpecifier, tycoSignifier.span() + span); return true; } bool parseNothingStatement(CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "nothing", &span)) return false; Space::assertCharacter(source, ';', &span); _lastSpan = span; return true; } bool parseIncrementDecrementStatement(Code code, CharacterSource* source) { Span span; Operator o = OperatorIncrement().parse(source, &span); if (!o.valid()) o = OperatorDecrement().parse(source, &span); if (!o.valid()) return false; Expression lValue = Expression::parse(source); Span span2; Space::assertCharacter(source, ';', &span2); _lastSpan = span2; code.insert(FunctionCallExpression::unary(o, span, FunctionCallExpression::unary( OperatorAmpersand(), Span(), lValue)), span + span2); return true; } void parseConditionalStatement2(Code code, CharacterSource* source, Span span, bool unlessStatement) { Space::assertCharacter(source, '('); Expression condition = Expression::parseOrFail(source); Space::assertCharacter(source, ')'); CodeList conditionalCode; parseStatementOrFail(conditionalCode, source); span += _lastSpan; CodeList elseCode; if (Space::parseKeyword(source, "else")) parseStatementOrFail(elseCode, source); else { if (Space::parseKeyword(source, "elseIf")) parseConditionalStatement2(elseCode, source, span, false); else { if (Space::parseKeyword(source, "elseUnless")) parseConditionalStatement2(elseCode, source, span, true); } } if (unlessStatement) condition = !condition; code.insert(condition, conditionalCode, elseCode, span + _lastSpan); } // ConditionalStatement = (`if` | `unless`) ConditionedStatement // ((`elseIf` | `elseUnless`) ConditionedStatement)* [`else` Statement]; // ConditionedStatement = "(" Expression ")" Statement; bool parseConditionalStatement(Code code, CharacterSource* source) { Span span; if (Space::parseKeyword(source, "if", &span)) { parseConditionalStatement2(code, source, span, false); return true; } if (Space::parseKeyword(source, "unless", &span)) { parseConditionalStatement2(code, source, span, true); return true; } return false; } SwitchStatement::Case parseCase(CharacterSource* source) { List expressions; bool defaultType; Span span; if (Space::parseKeyword(source, "case", &span)) { defaultType = false; do { Expression expression = Expression::parseOrFail(source); expressions.add(expression); if (!Space::parseCharacter(source, ',')) break; } while (true); } else { defaultType = true; if (!Space::parseKeyword(source, "default", &span)) source->location().throwError("Expected case or default"); } Space::assertCharacter(source, ':'); CodeList code; parseStatementOrFail(code, source); span += _lastSpan; if (defaultType) return SwitchStatement::Case(code, span); return SwitchStatement::Case(expressions, code, span); } bool parseSwitchStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "switch", &span)) return false; Space::assertCharacter(source, '('); Expression expression = Expression::parseOrFail(source); Space::assertCharacter(source, ')'); Space::assertCharacter(source, '{'); SwitchStatement::Case defaultCase; CharacterSource s = *source; List cases; do { SwitchStatement::Case c = parseCase(source); if (!c.valid()) break; if (c.isDefault()) { if (defaultCase.valid()) s.location().throwError( "This switch statement already has a default case"); defaultCase = c; } else cases.add(c); } while (true); Space::assertCharacter(source, '}', &span); _lastSpan = span; code.insert(expression, defaultCase, cases, span); return true; } bool parseReturnStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "return", &span)) return false; Expression expression = Expression::parseOrFail(source); Space::assertCharacter(source, ';', &span); _lastSpan = span; code.insert(expression, span); return true; } bool parseIncludeStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "include", &span)) return false; Expression expression = parseExpression(source); StringLiteralExpression s(expression); if (!s.valid()) { expression.span().throwError("Argument to include must be a " "simple string"); } String path = s.string(); //Resolver resolver; //resolver.resolve(expression); //Evaluator evaluator; //Value value = evaluator.evaluate(expression).convertTo(StringType()); //String path = value.value(); File lastFile = _currentFile; _currentFile = File(path, _currentFile.parent()); parseFromFile(code, _currentFile); _currentFile = lastFile; } bool parseBreakOrContinueStatement(Code code, CharacterSource* source) { int breakCount = 0; bool hasContinue = false; Span span; do { if (Space::parseKeyword(source, "break", &span)) ++breakCount; else break; } while (true); if (Space::parseKeyword(source, "continue", &span)) hasContinue = true; if (breakCount == 0 && !hasContinue) return false; Space::assertCharacter(source, ';', &span); _lastSpan = span; code.insert(span, breakCount, hasContinue); return true; } bool parseForeverStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "forever", &span)) return false; CodeList body; parseStatementOrFail(body, source); code.insert(body, span); return true; } bool parseWhileStatement(Code code, CharacterSource* source) { Span span; CodeList doCode; bool foundDo = false; if (Space::parseKeyword(source, "do", &span)) { foundDo = true; parseStatementOrFail(doCode, source); } bool foundWhile = false; bool foundUntil = false; if (Space::parseKeyword(source, "while", &span)) foundWhile = true; else if (Space::parseKeyword(source, "until", &span)) foundUntil = true; if (!foundWhile && !foundUntil) { if (foundDo) source->location().throwError("Expected while or until"); return false; } Space::assertCharacter(source, '('); Expression condition = Expression::parse(source); Space::assertCharacter(source, ')'); CodeList body; parseStatementOrFail(body, source); CodeList doneCode; if (Space::parseKeyword(source, "done")) parseStatementOrFail(doneCode, source); span += _lastSpan; if (foundUntil) condition = !condition; code.insert(doCode, condition, body, doneCode, span); return true; } bool parseForStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "for", &span)) return false; Space::assertCharacter(source, '('); CodeList preCode; if (!parseStatement(preCode, source)) Space::assertCharacter(source, ';'); Expression expression = Expression::parse(source); Space::assertCharacter(source, ';'); CodeList postCode; parseStatement(postCode, source); Space::parseCharacter(source, ')'); CodeList body; parseStatement(body, source); span += _lastSpan; CodeList doneCode; if (Space::parseKeyword(source, "done")) { parseStatementOrFail(doneCode, source); span += _lastSpan; } code.insert(preCode, expression, postCode, body, doneCode, span); return true; } bool parseLabelStatement(Code code, CharacterSource* source) { CharacterSource s2 = *source; Identifier identifier = Identifier::parse(&s2); if (!identifier.valid()) return false; Span span; if (!Space::parseCharacter(&s2, ':', &span)) return false; _lastSpan = span; code.insert(identifier, identifier.span() + span); return true; } bool parseGotoStatement(Code code, CharacterSource* source) { Span span; if (!Space::parseKeyword(source, "goto", &span)) return false; Expression expression = Expression::parseOrFail(source); Span span2; Space::parseCharacter(source, ';', &span); code.insert(expression, span); return true; } Span _lastSpan; File _currentFile; }; #endif // INCLUDED_PARSER_H