package me.august.lumen.compile.parser; import me.august.lumen.common.ModifierSet; import me.august.lumen.compile.codegen.BuildContext; import me.august.lumen.compile.parser.ast.*; import me.august.lumen.compile.parser.ast.expr.Expression; import me.august.lumen.compile.parser.ast.expr.NotExpr; import me.august.lumen.compile.parser.ast.expr.RangeExpr; import me.august.lumen.compile.parser.ast.stmt.*; import me.august.lumen.compile.resolve.type.UnresolvedType; import me.august.lumen.compile.scanner.Token; import me.august.lumen.compile.scanner.TokenSource; import me.august.lumen.compile.scanner.tokens.ImportPathToken; import java.util.ArrayList; import java.util.List; import static me.august.lumen.compile.scanner.Type.*; public class LumenParser extends ExpressionParser { public LumenParser(TokenSource tokenSource, BuildContext context) { super(tokenSource, context); } public LumenParser(TokenSource tokenSource) { super(tokenSource); } private ModifierSet parseModifiers() { ModifierSet modifiers = new ModifierSet(); while (peek().isModifier()) { modifiers.add(consume().getType().toModifier()); } return modifiers; } public ProgramNode parseProgram() { List<ImportNode> imports = new ArrayList<>(); ClassNode classNode = null; startRecording(); Token token = consume(); while (token.getType() != EOF) { // Handle import declaration if (token.getType() == IMPORT_KEYWORD) { ImportPathToken pathToken = (ImportPathToken) consume(); ImportNode node = new ImportNode(pathToken.getPath(), pathToken.getClasses()); imports.add(node); endRecording(node); // Handle class declaration } else if (token.getType() == CLASS_KEYWORD || token.isModifier()) { ModifierSet modifiers = new ModifierSet(); if (token.isModifier()) { modifiers.add(token.getType().toModifier()); modifiers.merge(parseModifiers()); expect(CLASS_KEYWORD); } else { modifiers.setPublic(true); } classNode = parseClass(modifiers); endRecording(classNode); } startRecording(); token = consume(); } stopRecording(); return new ProgramNode(imports, classNode); } private ClassNode parseClass(ModifierSet modifiers) { String name = consume().expectType(IDENTIFIER).getContent(); Typed superClass = new Typed(parseSuperclass()); String[] interfaces = parseImplementsInterfaces(); expect(L_BRACE); ClassNode classNode = new ClassNode(name, superClass, interfaces, modifiers); while (!accept(R_BRACE)) { parseMethodOrField(classNode); } return classNode; } private UnresolvedType parseSuperclass() { if (accept(COLON)) { return nextUnresolvedType(); } else { return UnresolvedType.OBJECT_TYPE; } } private String[] parseImplementsInterfaces() { if (accept(PLUS)) { List<String> interfaces = new ArrayList<>(); consume().expectType(L_PAREN); while (true) { Token token = consume(); String inter = token.expectType(IDENTIFIER).getContent(); interfaces.add(inter); token = consume(); if (token.getType() == R_PAREN) { break; } else { token.expectType(COMMA); } } return interfaces.toArray(new String[interfaces.size()]); } else { return new String[0]; } } private void parseMethodOrField(ClassNode classNode) { ModifierSet modifiers = new ModifierSet(); startRecording(); boolean hasAccessModifier = false; while (peek().isModifier()) { modifiers.add(consume().getType().toModifier()); hasAccessModifier = true; } if (!hasAccessModifier) { modifiers.setPublic(true); } Token token = peek(); if (token.getType() == IDENTIFIER) { FieldNode field = parseField(modifiers); classNode.getFields().add(field); endRecording(field); } else if (token.getType() == DEF_KEYWORD) { consume(); // consume 'def' MethodNode method = parseMethod(modifiers); classNode.getMethods().add(method); endRecording(method); } } private FieldNode parseField(ModifierSet modifiers) { String name = consume().expectType(IDENTIFIER).getContent(); expect(COLON); // consume ':' UnresolvedType type = nextUnresolvedType(); FieldNode field = new FieldNode(name, type, modifiers); if (accept(ASSIGN)) { field.setDefaultValue(parseExpression()); } return field; } private MethodNode parseMethod(ModifierSet modifiers) { String name = consume().expectType(IDENTIFIER).getContent(); UnresolvedType type; if (accept(COLON)) { type = nextUnresolvedType(); } else { type = UnresolvedType.VOID_TYPE; } List<Parameter> parameters = new ArrayList<>(); if (accept(L_PAREN)) { while (peek().getType() != R_PAREN) { String parameterName = consume().expectType(IDENTIFIER).getContent(); expect(COLON); UnresolvedType parameterType = nextUnresolvedType(); parameters.add(new Parameter(parameterName, parameterType)); accept(COMMA); } expect(R_PAREN); } MethodNode method = new MethodNode(name, type, parameters, modifiers); method.setBody(parseBody()); return method; } private Body parseBody() { Body body = new Body(); startRecording(); expect(L_BRACE); while (peek().getType() != R_BRACE) { body.addCode(parseStatement()); } expect(R_BRACE); return endRecording(body); } private CodeBlock parseStatement() { startRecording(); CodeBlock code; if (accept(VAR_KEYWORD)) { code = parseLocalVariable(); } else if (accept(L_BRACE)) { code = parseBody(); } else if (accept(IF_KEYWORD)) { code = parseIfStatement(false); } else if (accept(UNLESS_KEYWORD)) { code = parseIfStatement(true); } else if (accept(WHILE_KEYWORD)) { code = parseWhileStatement(false); } else if (accept(UNTIL_KEYWORD)) { code = parseWhileStatement(true); } else if (accept(EACH_KEYWORD)) { code = parseEachStatement(); } else if (accept(FOR_KEYWORD)) { code = parseForStatement(); } else if (accept(BREAK_KEYWORD)) { code = new BreakStmt(); } else if (accept(NEXT_KEYWORD)) { code = new NextStmt(); } else if (accept(RETURN_KEYWORD)) { code = new ReturnStmt(parseExpression()); } else { code = parseExpression(); } return endRecording(code); } private VarStmt parseLocalVariable() { String name = consume().expectType(IDENTIFIER).getContent(); expect(COLON); UnresolvedType type = nextUnresolvedType(); Expression value = accept(ASSIGN) ? parseExpression() : null; return new VarStmt(name, type, value); } public IfStmt parseIfStatement(boolean inverted) { Expression condition = parseExpression(); if (inverted) { // TODO optimize condition = new NotExpr(condition); } if (accept(THEN_KEYWORD)) { Body ifBranch = new Body(parseStatement()); Body elseBranch = accept(ELSE_KEYWORD) ? new Body(parseStatement()) : null; return new IfStmt(condition, ifBranch, new ArrayList<>(), elseBranch); } Body ifBranch = parseBody(); List<IfStmt.ElseIf> elseIfs = new ArrayList<>(); Body elseBranch = null; while (accept(ELSE_KEYWORD)) { if (accept(IF_KEYWORD)) { IfStmt.ElseIf elseIf = new IfStmt.ElseIf( parseExpression(), parseBody() ); elseIfs.add(elseIf); } else { // so meta if (elseBranch != null) throw new RuntimeException("There is already an else branch"); elseBranch = parseBody(); } } return new IfStmt(condition, ifBranch, elseIfs, elseBranch); } private WhileStmt parseWhileStatement(boolean inverted) { Expression condition = parseExpression(); if (inverted) { condition = new NotExpr(condition); } Body body; if (accept(DO_KEYWORD)) { body = new Body(parseStatement()); } else { body = parseBody(); } return new WhileStmt(condition, body); } private EachStmt parseEachStatement() { String identifier = consume().expectType(IDENTIFIER).getContent(); // "in" keyword (not really a keyword) consume().expectType(IDENTIFIER).expectContent("in"); Expression expression = parseExpression(); Body body; if (accept(DO_KEYWORD)) { body = new Body(parseStatement()); } else { body = parseBody(); } return new EachStmt(identifier, expression, body); } private ForStmt parseForStatement() { String ident = consume().expectType(IDENTIFIER).getContent(); consume().expectType(IDENTIFIER).expectContent("in"); Expression expr = parseExpression(); if (!(expr instanceof RangeExpr)) { getBuildContext().error("Expected range", false, expr); } RangeExpr range = (RangeExpr) expr; Body body; if (accept(DO_KEYWORD)) { body = new Body(parseStatement()); } else { body = parseBody(); } return new ForStmt(ident, range, body); } }