IF!!!
This commit is contained in:
		
							parent
							
								
									4adfed238e
								
							
						
					
					
						commit
						19c9409ccd
					
				
					 18 changed files with 375 additions and 209 deletions
				
			
		|  | @ -12,18 +12,14 @@ public class ElementWalker<T, E> { | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final BiPredicate<T, Integer> hasNextPredicate; |     private final BiPredicate<T, Integer> hasNextPredicate; | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final BiPredicate<T, Integer> hasPrevPredicate; |  | ||||||
|     @NotNull |  | ||||||
|     private final BiFunction<T, Integer, E> atFunction; |     private final BiFunction<T, Integer, E> atFunction; | ||||||
|     private int index = 0; |     private int index = 0; | ||||||
| 
 | 
 | ||||||
|     public ElementWalker(@NotNull final T back, |     public ElementWalker(@NotNull final T back, | ||||||
|                          @NotNull final BiPredicate<T, Integer> hasNextPredicate, |                          @NotNull final BiPredicate<T, Integer> hasNextPredicate, | ||||||
|                          @NotNull final BiPredicate<T, Integer> hasPrevPredicate, |  | ||||||
|                          @NotNull final BiFunction<T, Integer, E> atFunction) { |                          @NotNull final BiFunction<T, Integer, E> atFunction) { | ||||||
|         this.back = back; |         this.back = back; | ||||||
|         this.hasNextPredicate = hasNextPredicate; |         this.hasNextPredicate = hasNextPredicate; | ||||||
|         this.hasPrevPredicate = hasPrevPredicate; |  | ||||||
|         this.atFunction = atFunction; |         this.atFunction = atFunction; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -40,18 +36,4 @@ public class ElementWalker<T, E> { | ||||||
|     public Option<E> peekNext() { |     public Option<E> peekNext() { | ||||||
|         return Option.when(this.hasNext(), () -> this.atFunction.apply(this.back, this.index)); |         return Option.when(this.hasNext(), () -> this.atFunction.apply(this.back, this.index)); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public boolean hasPrev() { |  | ||||||
|         return this.hasPrevPredicate.test(this.back, this.index); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @NotNull |  | ||||||
|     public E prev() { |  | ||||||
|         return this.atFunction.apply(this.back, --this.index); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @NotNull |  | ||||||
|     public Option<E> peekPrev() { |  | ||||||
|         return Option.when(this.hasPrev(), () -> this.atFunction.apply(this.back, this.index - 1)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ public class SeqWalker<E> extends ElementWalker<Seq<E>, E> { | ||||||
|         super( |         super( | ||||||
|                 seq, |                 seq, | ||||||
|                 (back, index) -> index < back.length(), |                 (back, index) -> index < back.length(), | ||||||
|                 (back, index) -> index > 0, |  | ||||||
|                 Seq::get |                 Seq::get | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package ch.fritteli.gombaila.domain.common; | package ch.fritteli.gombaila.domain.common; | ||||||
| 
 | 
 | ||||||
| public sealed interface NodeStmt extends Node permits NodeStmtExit, NodeStmtLet, NodeStmtPrint { | public sealed interface NodeStmt extends Node permits NodeStmtAssign, NodeStmtExit, NodeStmtIf, NodeStmtLet, NodeStmtPrint, NodeStmtScope { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.common; | ||||||
|  | 
 | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | public record NodeStmtAssign(@NotNull Token ident, @NotNull NodeExpr expr) implements NodeStmt { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.common; | ||||||
|  | 
 | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | public record NodeStmtIf(@NotNull NodeExpr expr, @NotNull NodeStmtScope scope) implements NodeStmt { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -2,6 +2,6 @@ package ch.fritteli.gombaila.domain.common; | ||||||
| 
 | 
 | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public record NodeStmtLet(@NotNull Token ident, @NotNull NodeExpr expr) implements NodeStmt { | public record NodeStmtLet(@NotNull NodeStmtAssign stmtAssign) implements NodeStmt { | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.common; | ||||||
|  | 
 | ||||||
|  | import io.vavr.collection.Seq; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | public record NodeStmtScope(@NotNull Seq<NodeStmt> stmts) implements NodeStmt { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -3,8 +3,8 @@ package ch.fritteli.gombaila.domain.common; | ||||||
| import io.vavr.control.Option; | import io.vavr.control.Option; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public record Token(@NotNull TokenType type, @NotNull Option<Object> value) { | public record Token(@NotNull TokenType type, @NotNull Option<Object> value, int line, int column) { | ||||||
|     public Token(@NotNull final TokenType type) { |     public Token(@NotNull final TokenType type, final int line, final int column) { | ||||||
|         this(type, Option.none()); |         this(type, Option.none(), line, column); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ public enum TokenType { | ||||||
|     SEMI, |     SEMI, | ||||||
|     OPEN_PAREN, |     OPEN_PAREN, | ||||||
|     CLOSE_PAREN, |     CLOSE_PAREN, | ||||||
|  |     OPEN_CURLY, | ||||||
|  |     CLOSE_CURLY, | ||||||
|     EQUALS, |     EQUALS, | ||||||
|     PLUS, |     PLUS, | ||||||
|     MINUS, |     MINUS, | ||||||
|  | @ -23,7 +25,8 @@ public enum TokenType { | ||||||
|     INT_LIT, |     INT_LIT, | ||||||
|     IDENTIFIER, |     IDENTIFIER, | ||||||
|     // the rest |     // the rest | ||||||
|     WHITESPACE; |     WHITESPACE, | ||||||
|  |     IF; | ||||||
| 
 | 
 | ||||||
|     public boolean isBinaryOperator() { |     public boolean isBinaryOperator() { | ||||||
|         return switch (this) { |         return switch (this) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,100 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.generator; | ||||||
|  | 
 | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprAdd; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprDiv; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprExp; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprMinus; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprMod; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeBinExprMult; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeExpr; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeExprIdent; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeExprIntLit; | ||||||
|  | import io.vavr.control.Option; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | final class ExprVisitor { | ||||||
|  |     @NotNull | ||||||
|  |     private final Generator generator; | ||||||
|  | 
 | ||||||
|  |     ExprVisitor(@NotNull final Generator generator) { | ||||||
|  |         this.generator = generator; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void visit(@NotNull final NodeExpr expr) { | ||||||
|  |         switch (expr) { | ||||||
|  |             case final NodeExprIdent exprIdent -> visit(exprIdent); | ||||||
|  |             case final NodeExprIntLit exprIntLit -> visit(exprIntLit); | ||||||
|  |             case final NodeBinExprAdd exprAdd -> visit(exprAdd); | ||||||
|  |             case final NodeBinExprMinus exprMinus -> visit(exprMinus); | ||||||
|  |             case final NodeBinExprMult exprMult -> visit(exprMult); | ||||||
|  |             case final NodeBinExprDiv exprDiv -> visit(exprDiv); | ||||||
|  |             case final NodeBinExprMod exprMod -> visit(exprMod); | ||||||
|  |             case final NodeBinExprExp exprExp -> visit(exprExp); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeExprIdent expr) { | ||||||
|  |         final String varName = (String) expr.ident().value().get(); | ||||||
|  |         final Option<Variable> variable = this.generator.vars.find(var -> var.name().equals(varName)); | ||||||
|  | 
 | ||||||
|  |         if (variable.isEmpty()) { | ||||||
|  |             throw new GeneratorException("Undeclared identifier: %s".formatted(varName), expr); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         generator.push("qword [rsp+%d]".formatted(8 * (this.generator.stackSize - variable.get().stackLocation() - 1))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeExprIntLit expr) { | ||||||
|  |         this.generator.printer.commentedLine("store the value in rax", "mov", "rax, %s".formatted(expr.value().value().get())); | ||||||
|  |         generator.push("rax"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprAdd expr) { | ||||||
|  |         generator.generateExpr(expr.lhs()); | ||||||
|  |         generator.generateExpr(expr.rhs()); | ||||||
|  |         generator.pop("rbx"); | ||||||
|  |         generator.pop("rax"); | ||||||
|  |         generator.printer.commentedLine("add rbx to rax", "add", "rax, rbx"); | ||||||
|  |         generator.push("rax"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprMinus expr) { | ||||||
|  |         generator.generateExpr(expr.lhs()); | ||||||
|  |         generator.generateExpr(expr.rhs()); | ||||||
|  |         generator.pop("rbx"); | ||||||
|  |         generator.pop("rax"); | ||||||
|  |         generator.printer.commentedLine("subtract rbx from rax", "sub", "rax, rbx"); | ||||||
|  |         generator.push("rax"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprMult expr) { | ||||||
|  |         generator.generateExpr(expr.lhs()); | ||||||
|  |         generator.generateExpr(expr.rhs()); | ||||||
|  |         generator.pop("rbx"); | ||||||
|  |         generator.pop("rax"); | ||||||
|  |         generator.printer.commentedLine("multiply rax by rbx", "mul", "rbx"); | ||||||
|  |         generator.push("rax"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprDiv expr) { | ||||||
|  |         generator.generateExpr(expr.lhs()); | ||||||
|  |         generator.generateExpr(expr.rhs()); | ||||||
|  |         generator.pop("rbx"); | ||||||
|  |         generator.pop("rax"); | ||||||
|  |         generator.printer.commentedLine("divide rax by rbx", "div", "rbx"); | ||||||
|  |         generator.push("rax"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprMod expr) { | ||||||
|  |         generator.generateExpr(expr.lhs()); | ||||||
|  |         generator.generateExpr(expr.rhs()); | ||||||
|  |         generator.pop("rbx"); | ||||||
|  |         generator.pop("rax"); | ||||||
|  |         generator.printer.commentedLine("divide rax by rbx. The remainder will be in rdx.", "div", "rbx"); | ||||||
|  |         generator.push("rdx"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeBinExprExp expr) { | ||||||
|  |         throw new GeneratorException("Not yet implemented!", expr); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,29 +1,21 @@ | ||||||
| package ch.fritteli.gombaila.domain.generator; | package ch.fritteli.gombaila.domain.generator; | ||||||
| 
 | 
 | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprAdd; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprDiv; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprExp; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprMinus; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprMod; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeBinExprMult; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeExpr; | import ch.fritteli.gombaila.domain.common.NodeExpr; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeExprIdent; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeExprIntLit; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeProg; | import ch.fritteli.gombaila.domain.common.NodeProg; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmt; | import ch.fritteli.gombaila.domain.common.NodeStmt; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtExit; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtLet; |  | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtPrint; |  | ||||||
| import ch.fritteli.gombaila.domain.printer.Printer; | import ch.fritteli.gombaila.domain.printer.Printer; | ||||||
|  | import io.vavr.collection.List; | ||||||
|  | import io.vavr.collection.Seq; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| import java.time.ZonedDateTime; | import java.time.ZonedDateTime; | ||||||
| import java.util.HashMap; | import java.util.Stack; | ||||||
| import java.util.Map; |  | ||||||
| 
 | 
 | ||||||
| public class Generator { | public class Generator { | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final Printer printer = new Printer(); |     final Printer printer = new Printer(); | ||||||
|  |     @NotNull | ||||||
|  |     final Stack<Integer> scopes = new Stack<>(); | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final NodeProg nodeProg; |     private final NodeProg nodeProg; | ||||||
|     @NotNull |     @NotNull | ||||||
|  | @ -31,13 +23,13 @@ public class Generator { | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final StmtVisitor stmtVisitor; |     private final StmtVisitor stmtVisitor; | ||||||
|     @NotNull |     @NotNull | ||||||
|     private final Map<String, Variable> identifierStackposition = new HashMap<>(); |     Seq<Variable> vars = List.empty(); | ||||||
|     private int stackSize = 0; |     int stackSize = 0; | ||||||
| 
 | 
 | ||||||
|     public Generator(@NotNull final NodeProg nodeProg) { |     public Generator(@NotNull final NodeProg nodeProg) { | ||||||
|         this.nodeProg = nodeProg; |         this.nodeProg = nodeProg; | ||||||
|         this.exprVisitor = new ExprVisitor(); |         this.exprVisitor = new ExprVisitor(this); | ||||||
|         this.stmtVisitor = new StmtVisitor(); |         this.stmtVisitor = new StmtVisitor(this); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NotNull |     @NotNull | ||||||
|  | @ -75,149 +67,36 @@ public class Generator { | ||||||
|         this.printer.line("syscall"); |         this.printer.line("syscall"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void generateStmt(@NotNull final NodeStmt stmt) { |     void generateStmt(@NotNull final NodeStmt stmt) { | ||||||
|         this.stmtVisitor.visit(stmt); |         this.stmtVisitor.visit(stmt); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void generateExpr(@NotNull final NodeExpr expr) { |     void generateExpr(@NotNull final NodeExpr expr) { | ||||||
|         this.exprVisitor.visit(expr); |         this.exprVisitor.visit(expr); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void push(@NotNull final String reg) { |     void push(@NotNull final String reg) { | ||||||
|         this.printer.commentedLine("push %s onto top of the stack".formatted(reg), "push", reg); |         this.printer.commentedLine("push %s onto top of the stack".formatted(reg), "push", reg); | ||||||
|         this.stackSize++; |         this.stackSize++; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void pop(@NotNull final String reg) { |     void pop(@NotNull final String reg) { | ||||||
|         this.printer.commentedLine("pop top of the stack into %s".formatted(reg), "pop", reg); |         this.printer.commentedLine("pop top of the stack into %s".formatted(reg), "pop", reg); | ||||||
|         this.stackSize--; |         this.stackSize--; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private record Variable(int stackLocation) { |     void beginScope() { | ||||||
|  |         this.scopes.push(this.vars.size()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private final class StmtVisitor { |     void endScope() { | ||||||
| 
 |         final int popCount = this.vars.size() - this.scopes.pop(); | ||||||
|         private StmtVisitor() { |         if (popCount > 0) { | ||||||
|         } |             this.printer.commentedLine( | ||||||
| 
 |                     "Reset the stack pointer to discard out-of-scope variables", | ||||||
|         void visit(@NotNull final NodeStmt stmt) { |                     "add", "rsp, %d".formatted(popCount * 8)); | ||||||
|             switch (stmt) { |             this.stackSize -= popCount; | ||||||
|                 case final NodeStmtLet stmtLet -> visit(stmtLet); |  | ||||||
|                 case final NodeStmtExit stmtExit -> visit(stmtExit); |  | ||||||
|                 case final NodeStmtPrint stmtPrint -> visit(stmtPrint); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeStmtExit stmt) { |  | ||||||
|             generateExpr(stmt.expr()); |  | ||||||
|             pop("rdi"); |  | ||||||
|             printer.commentedLine("Jump to the default exit routine", "jmp", "exit"); |  | ||||||
| //            printer.commentedLine("Prepare for the EXIT syscall", "mov", "rax, SYSCALL_exit"); |  | ||||||
| //            printer.line("syscall"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeStmtLet stmt) { |  | ||||||
|             final Variable previousValue = identifierStackposition.put(((String) stmt.ident().value().get()), new Variable(stackSize)); |  | ||||||
|             if (previousValue != null) { |  | ||||||
|                 throw new GeneratorException("Identifier already used", stmt); |  | ||||||
|             } |  | ||||||
|             generateExpr(stmt.expr()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeStmtPrint stmt) { |  | ||||||
| //            generateExpr(stmt.nodeExpr()); |  | ||||||
| //            // rdx: length of output |  | ||||||
| //            line("mov rdx, 8"); |  | ||||||
| //            // rsi: output |  | ||||||
| //            pop("rsi"); |  | ||||||
| //            // rdi: fd-value (STDOUT=1) |  | ||||||
| //            line("mov rdi, 1"); |  | ||||||
| //            // rax: syscall (SYS_WRITE=1) |  | ||||||
| //            line("mov rax, 1"); |  | ||||||
| //            // syscall |  | ||||||
| //            line("syscall"); |  | ||||||
|             throw new GeneratorException("Not implemented yet", stmt); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private final class ExprVisitor { |  | ||||||
|         private ExprVisitor() { |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void visit(@NotNull final NodeExpr expr) { |  | ||||||
|             switch (expr) { |  | ||||||
|                 case final NodeExprIdent exprIdent -> visit(exprIdent); |  | ||||||
|                 case final NodeExprIntLit exprIntLit -> visit(exprIntLit); |  | ||||||
|                 case final NodeBinExprAdd exprAdd -> visit(exprAdd); |  | ||||||
|                 case final NodeBinExprMinus exprMinus -> visit(exprMinus); |  | ||||||
|                 case final NodeBinExprMult exprMult -> visit(exprMult); |  | ||||||
|                 case final NodeBinExprDiv exprDiv -> visit(exprDiv); |  | ||||||
|                 case final NodeBinExprMod exprMod -> visit(exprMod); |  | ||||||
|                 case final NodeBinExprExp exprExp -> visit(exprExp); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeExprIdent expr) { |  | ||||||
|             final Variable variable = identifierStackposition.get(expr.ident().value().get()); |  | ||||||
|             if (variable == null) { |  | ||||||
|                 throw new GeneratorException("Undeclared identifier", expr); |  | ||||||
|             } |  | ||||||
|             push("qword [rsp+%d]".formatted(8 * (stackSize - variable.stackLocation - 1))); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeExprIntLit expr) { |  | ||||||
|             printer.commentedLine("store the value in rax", "mov", "rax, %s".formatted(expr.value().value().get())); |  | ||||||
|             push("rax"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprAdd expr) { |  | ||||||
|             generateExpr(expr.lhs()); |  | ||||||
|             generateExpr(expr.rhs()); |  | ||||||
|             pop("rbx"); |  | ||||||
|             pop("rax"); |  | ||||||
|             printer.commentedLine("add rbx to rax", "add", "rax, rbx"); |  | ||||||
|             push("rax"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprMinus expr) { |  | ||||||
|             generateExpr(expr.lhs()); |  | ||||||
|             generateExpr(expr.rhs()); |  | ||||||
|             pop("rbx"); |  | ||||||
|             pop("rax"); |  | ||||||
|             printer.commentedLine("subtract rbx from rax", "sub", "rax, rbx"); |  | ||||||
|             push("rax"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprMult expr) { |  | ||||||
|             generateExpr(expr.lhs()); |  | ||||||
|             generateExpr(expr.rhs()); |  | ||||||
|             pop("rbx"); |  | ||||||
|             pop("rax"); |  | ||||||
|             printer.commentedLine("multiply rax by rbx", "mul", "rbx"); |  | ||||||
|             push("rax"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprDiv expr) { |  | ||||||
|             generateExpr(expr.lhs()); |  | ||||||
|             generateExpr(expr.rhs()); |  | ||||||
|             pop("rbx"); |  | ||||||
|             pop("rax"); |  | ||||||
|             printer.commentedLine("divide rax by rbx", "div", "rbx"); |  | ||||||
|             push("rax"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprMod expr) { |  | ||||||
|             generateExpr(expr.lhs()); |  | ||||||
|             generateExpr(expr.rhs()); |  | ||||||
|             pop("rbx"); |  | ||||||
|             pop("rax"); |  | ||||||
|             printer.commentedLine("divide rax by rbx. The remainder will be in rdx.", "div", "rbx"); |  | ||||||
|             push("rdx"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void visit(@NotNull final NodeBinExprExp expr) { |  | ||||||
|             throw new GeneratorException("Not yet implemented!", expr); |  | ||||||
|         } |         } | ||||||
|  |         this.vars = this.vars.dropRight(popCount); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,107 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.generator; | ||||||
|  | 
 | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmt; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtAssign; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtExit; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtIf; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtLet; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtPrint; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtScope; | ||||||
|  | import io.vavr.control.Option; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | final class StmtVisitor { | ||||||
|  |     @NotNull | ||||||
|  |     private final Generator generator; | ||||||
|  |     private int labelCounter = 0; | ||||||
|  | 
 | ||||||
|  |     StmtVisitor(@NotNull final Generator generator) { | ||||||
|  |         this.generator = generator; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void visit(@NotNull final NodeStmt stmt) { | ||||||
|  |         switch (stmt) { | ||||||
|  |             case final NodeStmtLet stmtLet -> visit(stmtLet); | ||||||
|  |             case final NodeStmtAssign stmtAssign -> visit(stmtAssign); | ||||||
|  |             case final NodeStmtExit stmtExit -> visit(stmtExit); | ||||||
|  |             case final NodeStmtPrint stmtPrint -> visit(stmtPrint); | ||||||
|  |             case final NodeStmtScope stmtScope -> visit(stmtScope); | ||||||
|  |             case final NodeStmtIf stmtIf -> visit(stmtIf); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtLet stmt) { | ||||||
|  |         final String varName = (String) stmt.stmtAssign().ident().value().get(); | ||||||
|  |         if (this.generator.vars.exists(var -> var.name().equals(varName))) { | ||||||
|  |             throw new GeneratorException("Identifier already used: %s".formatted(varName), stmt); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.generator.vars = this.generator.vars.append(new Variable(varName, generator.stackSize)); | ||||||
|  | 
 | ||||||
|  |         this.generator.generateExpr(stmt.stmtAssign().expr()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtAssign stmt) { | ||||||
|  |         final String varName = (String) stmt.ident().value().get(); | ||||||
|  |         final Option<Variable> variable = this.generator.vars.find(var -> var.name().equals(varName)); | ||||||
|  |         if (variable.isEmpty()) { | ||||||
|  |             throw new GeneratorException("Undeclared identifier: %s".formatted(varName), stmt); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         final int stackLocation = variable.get().stackLocation(); | ||||||
|  |         final int targetLocation = 8 * (this.generator.stackSize - stackLocation - 1); | ||||||
|  |         this.generator.generateExpr(stmt.expr()); | ||||||
|  |         this.generator.pop("rbx"); | ||||||
|  |         this.generator.printer.commentedLine( | ||||||
|  |                 "Overwrite the old value of %s in the stack", | ||||||
|  |                 "mov", | ||||||
|  |                 "[rsp+%d], rbx".formatted(targetLocation) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtExit stmt) { | ||||||
|  |         this.generator.generateExpr(stmt.expr()); | ||||||
|  |         this.generator.pop("rdi"); | ||||||
|  |         this.generator.printer.commentedLine("Jump to the default exit routine", "jmp", "exit"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtPrint stmt) { | ||||||
|  | //            generateExpr(stmt.nodeExpr()); | ||||||
|  | //            // rdx: length of output | ||||||
|  | //            line("mov rdx, 8"); | ||||||
|  | //            // rsi: output | ||||||
|  | //            pop("rsi"); | ||||||
|  | //            // rdi: fd-value (STDOUT=1) | ||||||
|  | //            line("mov rdi, 1"); | ||||||
|  | //            // rax: syscall (SYS_WRITE=1) | ||||||
|  | //            line("mov rax, 1"); | ||||||
|  | //            // syscall | ||||||
|  | //            line("syscall"); | ||||||
|  |         throw new GeneratorException("Not implemented yet", stmt); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtScope stmt) { | ||||||
|  |         this.generator.beginScope(); | ||||||
|  |         stmt.stmts().forEach(this.generator::generateStmt); | ||||||
|  |         this.generator.endScope(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void visit(@NotNull final NodeStmtIf stmt) { | ||||||
|  |         final String label = this.createNextLabel(); | ||||||
|  |         this.generator.generateExpr(stmt.expr()); | ||||||
|  |         this.generator.pop("rax"); | ||||||
|  |         this.generator.printer.commentedLine("test the condition", | ||||||
|  |                 "test", "rax, rax"); | ||||||
|  |         this.generator.printer.commentedLine( | ||||||
|  |                 "skip to %s if condition is not met".formatted(label), | ||||||
|  |                 "jz", label | ||||||
|  |         ); | ||||||
|  |         this.visit(stmt.scope()); | ||||||
|  |         this.generator.printer.label(label); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private String createNextLabel() { | ||||||
|  |         return "label" + (this.labelCounter++); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | package ch.fritteli.gombaila.domain.generator; | ||||||
|  | 
 | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
|  | record Variable(@NotNull String name, int stackLocation) { | ||||||
|  | } | ||||||
|  | @ -23,44 +23,50 @@ public class Lexer { | ||||||
|         Option<Character> next; |         Option<Character> next; | ||||||
|         while ((next = this.content.peekNext()).isDefined()) { |         while ((next = this.content.peekNext()).isDefined()) { | ||||||
|             final char c = next.get(); |             final char c = next.get(); | ||||||
|  |             final int line = this.content.line(); | ||||||
|  |             final int column = this.content.column(); | ||||||
|             if (Character.isAlphabetic(c) || c == '_') { |             if (Character.isAlphabetic(c) || c == '_') { | ||||||
|                 this.handleAlphabeticOrUnderscore(); |                 this.handleAlphabeticOrUnderscore(line, column); | ||||||
|             } else if (Character.isDigit(c)) { |             } else if (Character.isDigit(c)) { | ||||||
|                 this.handleDigit(); |                 this.handleDigit(line, column); | ||||||
|             } else if (Character.isWhitespace(c)) { |             } else if (Character.isWhitespace(c)) { | ||||||
|                 this.handleWhitespace(); |                 this.handleWhitespace(line, column); | ||||||
|             } else if (c == ';') { |             } else if (c == ';') { | ||||||
|                 this.handleSimple(TokenType.SEMI); |                 this.handleSimple(TokenType.SEMI, line, column); | ||||||
|             } else if (c == '(') { |             } else if (c == '(') { | ||||||
|                 this.handleSimple(TokenType.OPEN_PAREN); |                 this.handleSimple(TokenType.OPEN_PAREN, line, column); | ||||||
|             } else if (c == ')') { |             } else if (c == ')') { | ||||||
|                 this.handleSimple(TokenType.CLOSE_PAREN); |                 this.handleSimple(TokenType.CLOSE_PAREN, line, column); | ||||||
|  |             } else if (c == '{') { | ||||||
|  |                 this.handleSimple(TokenType.OPEN_CURLY, line, column); | ||||||
|  |             } else if (c == '}') { | ||||||
|  |                 this.handleSimple(TokenType.CLOSE_CURLY, line, column); | ||||||
|             } else if (c == '=') { |             } else if (c == '=') { | ||||||
|                 this.handleSimple(TokenType.EQUALS); |                 this.handleSimple(TokenType.EQUALS, line, column); | ||||||
|             } else if (c == '+') { |             } else if (c == '+') { | ||||||
|                 this.handleSimple(TokenType.PLUS); |                 this.handleSimple(TokenType.PLUS, line, column); | ||||||
|             } else if (c == '-') { |             } else if (c == '-') { | ||||||
|                 this.handleSimple(TokenType.MINUS); |                 this.handleSimple(TokenType.MINUS, line, column); | ||||||
|             } else if (c == '*') { |             } else if (c == '*') { | ||||||
|                 this.handleSimple(TokenType.MULT); |                 this.handleSimple(TokenType.MULT, line, column); | ||||||
|             } else if (c == '/') { |             } else if (c == '/') { | ||||||
|                 this.handleSimple(TokenType.DIV); |                 this.handleSimple(TokenType.DIV, line, column); | ||||||
|             } else if (c == '%') { |             } else if (c == '%') { | ||||||
|                 this.handleSimple(TokenType.MOD); |                 this.handleSimple(TokenType.MOD, line, column); | ||||||
|             } else if (c == '^') { |             } else if (c == '^') { | ||||||
|                 this.handleSimple(TokenType.EXP); |                 this.handleSimple(TokenType.EXP, line, column); | ||||||
|             } else { |             } else { | ||||||
|                 throw this.error(c); |                 throw this.error(c, line, column); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return this.tokens; |         return this.tokens; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private LexerException error(final char c) { |     private LexerException error(final char c, final int line, final int column) { | ||||||
|         return new LexerException("Error parsing input: Unexpected character '%c'.".formatted(c), -1, -1); |         return new LexerException("Error parsing input: Unexpected character '%c'.".formatted(c), line, column); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void handleAlphabeticOrUnderscore() { |     private void handleAlphabeticOrUnderscore(final int line, final int column) { | ||||||
|         final StringBuilder s = new StringBuilder(); |         final StringBuilder s = new StringBuilder(); | ||||||
|         while (this.content.peekNext().exists(Predicates.<Character>anyOf( |         while (this.content.peekNext().exists(Predicates.<Character>anyOf( | ||||||
|                 Character::isAlphabetic, |                 Character::isAlphabetic, | ||||||
|  | @ -70,32 +76,34 @@ public class Lexer { | ||||||
|             s.append(this.content.next()); |             s.append(this.content.next()); | ||||||
|         } |         } | ||||||
|         switch (s.toString()) { |         switch (s.toString()) { | ||||||
|             case "exit" -> this.appendToken(new Token(TokenType.EXIT)); |             case "exit" -> this.appendToken(new Token(TokenType.EXIT, line, column)); | ||||||
|             case "let" -> this.appendToken(new Token(TokenType.LET)); |             case "let" -> this.appendToken(new Token(TokenType.LET, line, column)); | ||||||
|             case "print" -> this.appendToken(new Token(TokenType.PRINT)); |             case "print" -> this.appendToken(new Token(TokenType.PRINT, line, column)); | ||||||
|             case final String value -> this.appendToken(new Token(TokenType.IDENTIFIER, Option.of(value))); |             case "if" -> this.appendToken(new Token(TokenType.IF, line, column)); | ||||||
|  |             case final String value -> | ||||||
|  |                     this.appendToken(new Token(TokenType.IDENTIFIER, Option.of(value), line, column)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void handleDigit() { |     private void handleDigit(final int line, final int column) { | ||||||
|         final StringBuilder s = new StringBuilder(); |         final StringBuilder s = new StringBuilder(); | ||||||
|         while (this.content.peekNext().exists(Character::isDigit)) { |         while (this.content.peekNext().exists(Character::isDigit)) { | ||||||
|             s.append(this.content.next()); |             s.append(this.content.next()); | ||||||
|         } |         } | ||||||
|         this.appendToken(new Token(TokenType.INT_LIT, Option.of(Long.parseLong(s.toString())))); |         this.appendToken(new Token(TokenType.INT_LIT, Option.of(Long.parseLong(s.toString())), line, column)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void handleWhitespace() { |     private void handleWhitespace(final int line, final int column) { | ||||||
|         final StringBuilder s = new StringBuilder(); |         final StringBuilder s = new StringBuilder(); | ||||||
|         while (this.content.peekNext().exists(Character::isWhitespace)) { |         while (this.content.peekNext().exists(Character::isWhitespace)) { | ||||||
|             s.append(this.content.next()); |             s.append(this.content.next()); | ||||||
|         } |         } | ||||||
|         this.appendToken(new Token(TokenType.WHITESPACE, Option.of(s.toString()))); |         this.appendToken(new Token(TokenType.WHITESPACE, Option.of(s.toString()), line, column)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void handleSimple(@NotNull final TokenType tokenType) { |     private void handleSimple(@NotNull final TokenType tokenType, final int line, final int column) { | ||||||
|         this.content.next(); |         this.content.next(); | ||||||
|         this.appendToken(new Token(tokenType)); |         this.appendToken(new Token(tokenType, line, column)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void appendToken(@NotNull final Token token) { |     private void appendToken(@NotNull final Token token) { | ||||||
|  |  | ||||||
|  | @ -4,13 +4,35 @@ import ch.fritteli.gombaila.ElementWalker; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| public class StringWalker extends ElementWalker<String, Character> { | public class StringWalker extends ElementWalker<String, Character> { | ||||||
|  |     private int line = 0; | ||||||
|  |     private int column = 0; | ||||||
| 
 | 
 | ||||||
|     public StringWalker(@NotNull final String string) { |     public StringWalker(@NotNull final String string) { | ||||||
|         super( |         super( | ||||||
|                 string, |                 string, | ||||||
|                 (back, index) -> index < back.length(), |                 (back, index) -> index < back.length(), | ||||||
|                 (back, index) -> index > 0, |  | ||||||
|                 String::charAt |                 String::charAt | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public int line() { | ||||||
|  |         return this.line + 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int column() { | ||||||
|  |         return this.column + 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NotNull | ||||||
|  |     @Override | ||||||
|  |     public Character next() { | ||||||
|  |         final char c = super.next(); | ||||||
|  |         if (c == '\n') { | ||||||
|  |             this.line++; | ||||||
|  |             this.column = 0; | ||||||
|  |         } else { | ||||||
|  |             this.column++; | ||||||
|  |         } | ||||||
|  |         return c; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,9 +12,12 @@ import ch.fritteli.gombaila.domain.common.NodeExprIdent; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeExprIntLit; | import ch.fritteli.gombaila.domain.common.NodeExprIntLit; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeProg; | import ch.fritteli.gombaila.domain.common.NodeProg; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmt; | import ch.fritteli.gombaila.domain.common.NodeStmt; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtAssign; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtExit; | import ch.fritteli.gombaila.domain.common.NodeStmtExit; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtIf; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtLet; | import ch.fritteli.gombaila.domain.common.NodeStmtLet; | ||||||
| import ch.fritteli.gombaila.domain.common.NodeStmtPrint; | import ch.fritteli.gombaila.domain.common.NodeStmtPrint; | ||||||
|  | import ch.fritteli.gombaila.domain.common.NodeStmtScope; | ||||||
| import ch.fritteli.gombaila.domain.common.Token; | import ch.fritteli.gombaila.domain.common.Token; | ||||||
| import ch.fritteli.gombaila.domain.common.TokenType; | import ch.fritteli.gombaila.domain.common.TokenType; | ||||||
| import io.vavr.collection.List; | import io.vavr.collection.List; | ||||||
|  | @ -26,7 +29,6 @@ public class Parser { | ||||||
|     private final SeqWalker<Token> tokens; |     private final SeqWalker<Token> tokens; | ||||||
| 
 | 
 | ||||||
|     public Parser(@NotNull final Seq<Token> tokens) { |     public Parser(@NotNull final Seq<Token> tokens) { | ||||||
| 
 |  | ||||||
|         this.tokens = new SeqWalker<>(tokens.reject(token -> TokenType.WHITESPACE.equals(token.type()))); |         this.tokens = new SeqWalker<>(tokens.reject(token -> TokenType.WHITESPACE.equals(token.type()))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -49,23 +51,58 @@ public class Parser { | ||||||
|             this.assertAndConsumeNextTokenType(TokenType.CLOSE_PAREN); |             this.assertAndConsumeNextTokenType(TokenType.CLOSE_PAREN); | ||||||
|             result = new NodeStmtExit(nodeExpr); |             result = new NodeStmtExit(nodeExpr); | ||||||
|         } else if (this.checkNextTokenTypeConsuming(TokenType.LET)) { |         } else if (this.checkNextTokenTypeConsuming(TokenType.LET)) { | ||||||
|             final Token identifier = this.assertAndConsumeNextTokenType(TokenType.IDENTIFIER); |             final NodeStmtAssign nodeStmtAssign = this.parseStmtAssign(); | ||||||
|             this.assertAndConsumeNextTokenType(TokenType.EQUALS); |             result = new NodeStmtLet(nodeStmtAssign); | ||||||
|             final NodeExpr nodeExpr = this.parseExpr(1); |  | ||||||
|             result = new NodeStmtLet(identifier, nodeExpr); |  | ||||||
|         } else if (this.checkNextTokenTypeConsuming(TokenType.PRINT)) { |         } else if (this.checkNextTokenTypeConsuming(TokenType.PRINT)) { | ||||||
|             this.assertAndConsumeNextTokenType(TokenType.OPEN_PAREN); |             this.assertAndConsumeNextTokenType(TokenType.OPEN_PAREN); | ||||||
|             final NodeExpr nodeExpr = this.parseExpr(1); |             final NodeExpr nodeExpr = this.parseExpr(1); | ||||||
|             this.assertAndConsumeNextTokenType(TokenType.CLOSE_PAREN); |             this.assertAndConsumeNextTokenType(TokenType.CLOSE_PAREN); | ||||||
|             result = new NodeStmtPrint(nodeExpr); |             result = new NodeStmtPrint(nodeExpr); | ||||||
|  |         } else if (this.checkNextTokenType(TokenType.OPEN_CURLY)) { | ||||||
|  |             // NB: We do NOT expect a SEMI here, so we return directly. | ||||||
|  |             return this.parseScope(); | ||||||
|  |         } else if (this.checkNextTokenTypeConsuming(TokenType.IF)) { | ||||||
|  |             this.assertAndConsumeNextTokenType(TokenType.OPEN_PAREN); | ||||||
|  |             final NodeExpr expr = this.parseExpr(1); | ||||||
|  |             this.assertAndConsumeNextTokenType(TokenType.CLOSE_PAREN); | ||||||
|  |             final NodeStmtScope scope = this.parseScope(); | ||||||
|  |             // NB: We do NOT expect a SEMI here, so we return directly. | ||||||
|  |             return new NodeStmtIf(expr, scope); | ||||||
|  |         } else if (this.checkNextTokenType(TokenType.IDENTIFIER)) { | ||||||
|  |             result = this.parseStmtAssign(); | ||||||
|         } else { |         } else { | ||||||
|             throw new ParserException("Could not parse statement", null); |             throw new ParserException("Could not parse statement", this.tokens.peekNext().getOrNull()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.assertAndConsumeNextTokenType(TokenType.SEMI); |         this.assertAndConsumeNextTokenType(TokenType.SEMI); | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @NotNull | ||||||
|  |     private NodeStmtAssign parseStmtAssign() { | ||||||
|  |         final Token identifier = this.assertAndConsumeNextTokenType(TokenType.IDENTIFIER); | ||||||
|  |         this.assertAndConsumeNextTokenType(TokenType.EQUALS); | ||||||
|  |         final NodeExpr nodeExpr = this.parseExpr(1); | ||||||
|  |         return new NodeStmtAssign(identifier, nodeExpr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NotNull | ||||||
|  |     private NodeStmtScope parseScope() { | ||||||
|  |         this.assertAndConsumeNextTokenType(TokenType.OPEN_CURLY); | ||||||
|  |         final Seq<NodeStmt> nodeStmts = this.parseStatements(); | ||||||
|  |         this.assertAndConsumeNextTokenType(TokenType.CLOSE_CURLY); | ||||||
|  |         return new NodeStmtScope(nodeStmts); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NotNull | ||||||
|  |     private Seq<NodeStmt> parseStatements() { | ||||||
|  |         Seq<NodeStmt> result = List.empty(); | ||||||
|  |         while (!this.checkNextTokenType(TokenType.CLOSE_CURLY)) { | ||||||
|  |             result = result.append(this.parseStmt()); | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @NotNull |     @NotNull | ||||||
|     private NodeExpr parseExpr(final int minPrecedence) { |     private NodeExpr parseExpr(final int minPrecedence) { | ||||||
|         NodeExpr result; |         NodeExpr result; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ package ch.fritteli.gombaila.domain.printer; | ||||||
| 
 | 
 | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| 
 | 
 | ||||||
| record RawLine(@NotNull String line, int indentationLevel, @NotNull WidthMap widthMap) implements Line{ | record RawLine(@NotNull String line, int indentationLevel, @NotNull WidthMap widthMap) implements Line { | ||||||
|     @Override |     @Override | ||||||
|     public void updateWithMap() { |     public void updateWithMap() { | ||||||
|         // nop |         // nop | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| let foo = 10; | let x = (10 - 2 * 3) / 2; | ||||||
| let bar = 2; | if (x-2) { | ||||||
| let baz = foo / bar; |     exit (69); | ||||||
| exit(baz); | } | ||||||
|  | exit(1); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue