From 0d820c8a911f3f3b7a21eb9e03b462aba1366c74 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 30 Sep 2023 23:53:55 +0300 Subject: [PATCH] Introduce stages for each main feature --- .../annimon/ownlang/stages/ScopedStage.java | 29 ++++++ .../ownlang/stages/ScopedStageFactory.java | 17 ++++ .../com/annimon/ownlang/stages/Stage.java | 16 ++++ .../annimon/ownlang/stages/StagesData.java | 8 ++ .../annimon/ownlang/stages/StagesDataMap.java | 19 ++++ .../main/java/com/annimon/ownlang/Main.java | 94 +++++++------------ .../ownlang/stages/ExecutionStage.java | 12 +++ .../ownlang/stages/FunctionAddingStage.java | 13 +++ .../annimon/ownlang/stages/LexerStage.java | 18 ++++ .../annimon/ownlang/stages/LinterStage.java | 13 +++ .../ownlang/stages/OptimizationStage.java | 16 ++++ .../annimon/ownlang/stages/ParserStage.java | 26 +++++ .../ownlang/stages/SourceLoaderStage.java | 21 +++++ .../ownlang/utils/TimeMeasurement.java | 37 +++----- 14 files changed, 255 insertions(+), 84 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java new file mode 100644 index 0000000..1660dfe --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.stages; + +import java.util.function.Consumer; + +public class ScopedStage implements Stage { + + private final String stageName; + private final Stage stage; + private final Consumer startStage; + private final Consumer endStage; + + ScopedStage(String stageName, Stage stage, + Consumer startStage, Consumer endStage) { + this.stageName = stageName; + this.stage = stage; + this.startStage = startStage; + this.endStage = endStage; + } + + @Override + public R perform(StagesData stagesData, I input) { + try { + startStage.accept(stageName); + return stage.perform(stagesData, input); + } finally { + endStage.accept(stageName); + } + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java new file mode 100644 index 0000000..f6d91fb --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.stages; + +import java.util.function.Consumer; + +public final class ScopedStageFactory { + private final Consumer startStage; + private final Consumer endStage; + + public ScopedStageFactory(Consumer startStage, Consumer endStage) { + this.startStage = startStage; + this.endStage = endStage; + } + + public ScopedStage create(String stageName, Stage stage) { + return new ScopedStage<>(stageName, stage, startStage, endStage); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java new file mode 100644 index 0000000..f2a3820 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java @@ -0,0 +1,16 @@ +package com.annimon.ownlang.stages; + +public interface Stage { + R perform(StagesData stagesData, I input); + + default Stage then(Stage next) { + return (scope, input) -> { + R result = perform(scope, input); + return next.perform(scope, result); + }; + } + + default Stage thenConditional(boolean enabled, Stage next) { + return enabled ? then(next) : this; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java new file mode 100644 index 0000000..b4aadbd --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.stages; + +public interface StagesData { + + T get(String tag); + + void put(String tag, Object input); +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java new file mode 100644 index 0000000..f1be63e --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.stages; + +import java.util.HashMap; +import java.util.Map; + +public class StagesDataMap implements StagesData { + private final Map data = new HashMap<>(); + + @SuppressWarnings("unchecked") + @Override + public T get(String tag) { + return (T) data.get(tag); + } + + @Override + public void put(String tag, Object input) { + data.put(tag, input); + } +} diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index d957c7c..517748a 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -1,15 +1,10 @@ package com.annimon.ownlang; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; -import com.annimon.ownlang.parser.Beautifier; -import com.annimon.ownlang.parser.Lexer; -import com.annimon.ownlang.parser.Linter; -import com.annimon.ownlang.parser.Optimizer; -import com.annimon.ownlang.parser.Parser; -import com.annimon.ownlang.parser.SourceLoader; -import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.parser.visitors.FunctionAdder; +import com.annimon.ownlang.stages.*; import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.TimeMeasurement; @@ -140,57 +135,43 @@ public final class Main { System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); Shared.setOwnlangArgs(ownlangArgs); } - + private static void run(String input, RunOptions options) { - options.validate(); - final TimeMeasurement measurement = new TimeMeasurement(); - measurement.start("Tokenize time"); - final List tokens = Lexer.tokenize(input); - measurement.stop("Tokenize time"); - if (options.showTokens) { - final int tokensCount = tokens.size(); - for (int i = 0; i < tokensCount; i++) { - System.out.println(i + " " + tokens.get(i)); - } - } - - measurement.start("Parse time"); - final Parser parser = new Parser(tokens); - final Statement parsedProgram = parser.parse(); - measurement.stop("Parse time"); - if (options.showAst) { - System.out.println(parsedProgram); - } - if (parser.getParseErrors().hasErrors()) { - System.out.println(parser.getParseErrors()); - return; - } - if (options.lintMode) { - Linter.lint(parsedProgram); - return; - } - final Statement program; - if (options.optimizationLevel > 0) { - measurement.start("Optimization time"); - program = Optimizer.optimize(parsedProgram, options.optimizationLevel, options.showAst); - measurement.stop("Optimization time"); - if (options.showAst) { - System.out.println(program.toString()); - } - } else { - program = parsedProgram; - } - program.accept(new FunctionAdder()); + final var measurement = new TimeMeasurement(); + final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop); + + final var stagesData = new StagesDataMap(); + stagesData.put(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, options.showAst); + stagesData.put(SourceLoaderStage.TAG_SOURCE, input); try { - measurement.start("Execution time"); - program.execute(); + scopedStages.create("Lexer", new LexerStage()) + .then(scopedStages.create("Parser", new ParserStage())) + .thenConditional(options.optimizationLevel > 0, + scopedStages.create("Optimization", new OptimizationStage(options.optimizationLevel))) + .thenConditional(options.lintMode, + scopedStages.create("Linter", new LinterStage())) + .then(scopedStages.create("Function adding", new FunctionAddingStage())) + .then(scopedStages.create("Execution", new ExecutionStage())) + .perform(stagesData, input); + } catch (OwnLangParserException ex) { + System.err.println(ex.getParseErrors()); } catch (StoppedException ex) { // skip } catch (Exception ex) { Console.handleException(Thread.currentThread(), ex); } finally { - if (options.showMeasurements) { - measurement.stop("Execution time"); + if (options.showTokens) { + final List tokens = stagesData.get(LexerStage.TAG_TOKENS); + int i = 0; + for (Token token : tokens) { + System.out.println(i++ + " " + token); + } + } + if (options.showAst) { + Statement program = stagesData.get(ParserStage.TAG_PROGRAM); + System.out.println(program); + } + if (!options.showMeasurements) { System.out.println("======================"); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); } @@ -211,14 +192,5 @@ public final class Main { beautifyMode = false; optimizationLevel = 0; } - - void validate() { - if (lintMode) { - showTokens = false; - showAst = false; - showMeasurements = false; - optimizationLevel = 0; - } - } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java new file mode 100644 index 0000000..2d7b91a --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.ast.Statement; + +public class ExecutionStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + input.execute(); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java new file mode 100644 index 0000000..28d917d --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.visitors.FunctionAdder; + +public class FunctionAddingStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + input.accept(new FunctionAdder()); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java new file mode 100644 index 0000000..ca77627 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Token; +import java.util.List; + +public class LexerStage implements Stage> { + + public static final String TAG_TOKENS = "tokens"; + + @Override + public List perform(StagesData stagesData, String input) { + Lexer lexer = new Lexer(input); + List tokens = lexer.tokenize(); + stagesData.put(TAG_TOKENS, tokens); + return tokens; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java new file mode 100644 index 0000000..66cbc96 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Linter; +import com.annimon.ownlang.parser.ast.Statement; + +public class LinterStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + Linter.lint(input); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java new file mode 100644 index 0000000..145309b --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java @@ -0,0 +1,16 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Optimizer; +import com.annimon.ownlang.parser.ast.Statement; + +public record OptimizationStage(int level) + implements Stage { + + public static final String TAG_OPTIMIZATION_SUMMARY = "optimizationSummary"; + + @Override + public Statement perform(StagesData stagesData, Statement input) { + boolean showSummary = stagesData.get(TAG_OPTIMIZATION_SUMMARY); + return Optimizer.optimize(input, level, showSummary); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java new file mode 100644 index 0000000..7e22fa6 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java @@ -0,0 +1,26 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.exceptions.OwnLangParserException; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.Statement; +import java.util.List; + +public class ParserStage implements Stage, Statement> { + + public static final String TAG_PROGRAM = "program"; + public static final String TAG_HAS_PARSE_ERRORS = "hasParseErrors"; + + @Override + public Statement perform(StagesData stagesData, List input) { + final Parser parser = new Parser(input); + final Statement program = parser.parse(); + final var parseErrors = parser.getParseErrors(); + stagesData.put(TAG_PROGRAM, program); + stagesData.put(TAG_HAS_PARSE_ERRORS, parseErrors.hasErrors()); + if (parseErrors.hasErrors()) { + throw new OwnLangParserException(parseErrors); + } + return program; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java new file mode 100644 index 0000000..a9d70fc --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.parser.SourceLoader; +import java.io.IOException; + +public class SourceLoaderStage implements Stage { + + public static final String TAG_SOURCE = "source"; + + @Override + public String perform(StagesData stagesData, String input) { + try { + String result = SourceLoader.readSource(input); + stagesData.put(TAG_SOURCE, result); + return result; + } catch (IOException e) { + throw new OwnLangRuntimeException("Unable to read input " + input, e); + } + } +} diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java index 2c1e4e2..ca39ca5 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.utils; import com.annimon.ownlang.Console; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -10,8 +10,8 @@ public final class TimeMeasurement { private final Map finished, running; public TimeMeasurement() { - finished = new HashMap<>(); - running = new HashMap<>(); + finished = new LinkedHashMap<>(); + running = new LinkedHashMap<>(); } public void clear() { @@ -19,29 +19,20 @@ public final class TimeMeasurement { running.clear(); } - public void start(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - running.put(name, time); + public void start(String name) { + running.put(name, System.nanoTime()); + } + + public void pause(String name) { + if (running.containsKey(name)) { + addTime(name, System.nanoTime() - running.get(name)); + running.remove(name); } } - public void pause(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - if (running.containsKey(name)) { - addTime(name, time - running.get(name)); - running.remove(name); - } - } - } - - public void stop(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - if (running.containsKey(name)) { - addTime(name, time - running.get(name)); - } + public void stop(String name) { + if (running.containsKey(name)) { + addTime(name, System.nanoTime() - running.get(name)); } }