Introduce stages for each main feature

This commit is contained in:
aNNiMON 2023-09-30 23:53:55 +03:00 committed by Victor Melnik
parent ea38227b44
commit 0d820c8a91
14 changed files with 255 additions and 84 deletions

View File

@ -0,0 +1,29 @@
package com.annimon.ownlang.stages;
import java.util.function.Consumer;
public class ScopedStage<I, R> implements Stage<I, R> {
private final String stageName;
private final Stage<? super I, ? extends R> stage;
private final Consumer<String> startStage;
private final Consumer<String> endStage;
ScopedStage(String stageName, Stage<? super I, ? extends R> stage,
Consumer<String> startStage, Consumer<String> 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);
}
}
}

View File

@ -0,0 +1,17 @@
package com.annimon.ownlang.stages;
import java.util.function.Consumer;
public final class ScopedStageFactory {
private final Consumer<String> startStage;
private final Consumer<String> endStage;
public ScopedStageFactory(Consumer<String> startStage, Consumer<String> endStage) {
this.startStage = startStage;
this.endStage = endStage;
}
public <I, R> ScopedStage<I, R> create(String stageName, Stage<? super I, ? extends R> stage) {
return new ScopedStage<>(stageName, stage, startStage, endStage);
}
}

View File

@ -0,0 +1,16 @@
package com.annimon.ownlang.stages;
public interface Stage<I, R> {
R perform(StagesData stagesData, I input);
default <NR> Stage<I, NR> then(Stage<? super R, ? extends NR> next) {
return (scope, input) -> {
R result = perform(scope, input);
return next.perform(scope, result);
};
}
default Stage<I, R> thenConditional(boolean enabled, Stage<? super R, ? extends R> next) {
return enabled ? then(next) : this;
}
}

View File

@ -0,0 +1,8 @@
package com.annimon.ownlang.stages;
public interface StagesData {
<T> T get(String tag);
void put(String tag, Object input);
}

View File

@ -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<String, Object> data = new HashMap<>();
@SuppressWarnings("unchecked")
@Override
public <T> T get(String tag) {
return (T) data.get(tag);
}
@Override
public void put(String tag, Object input) {
data.put(tag, input);
}
}

View File

@ -1,15 +1,10 @@
package com.annimon.ownlang; package com.annimon.ownlang;
import com.annimon.ownlang.exceptions.OwnLangParserException;
import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.exceptions.StoppedException;
import com.annimon.ownlang.parser.Beautifier; import com.annimon.ownlang.parser.*;
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.ast.Statement; 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.Repl;
import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.Sandbox;
import com.annimon.ownlang.utils.TimeMeasurement; import com.annimon.ownlang.utils.TimeMeasurement;
@ -140,57 +135,43 @@ public final class Main {
System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length);
Shared.setOwnlangArgs(ownlangArgs); Shared.setOwnlangArgs(ownlangArgs);
} }
private static void run(String input, RunOptions options) { private static void run(String input, RunOptions options) {
options.validate(); final var measurement = new TimeMeasurement();
final TimeMeasurement measurement = new TimeMeasurement(); final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop);
measurement.start("Tokenize time");
final List<Token> tokens = Lexer.tokenize(input); final var stagesData = new StagesDataMap();
measurement.stop("Tokenize time"); stagesData.put(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, options.showAst);
if (options.showTokens) { stagesData.put(SourceLoaderStage.TAG_SOURCE, input);
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());
try { try {
measurement.start("Execution time"); scopedStages.create("Lexer", new LexerStage())
program.execute(); .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) { } catch (StoppedException ex) {
// skip // skip
} catch (Exception ex) { } catch (Exception ex) {
Console.handleException(Thread.currentThread(), ex); Console.handleException(Thread.currentThread(), ex);
} finally { } finally {
if (options.showMeasurements) { if (options.showTokens) {
measurement.stop("Execution time"); final List<Token> 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("======================");
System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true));
} }
@ -211,14 +192,5 @@ public final class Main {
beautifyMode = false; beautifyMode = false;
optimizationLevel = 0; optimizationLevel = 0;
} }
void validate() {
if (lintMode) {
showTokens = false;
showAst = false;
showMeasurements = false;
optimizationLevel = 0;
}
}
} }
} }

View File

@ -0,0 +1,12 @@
package com.annimon.ownlang.stages;
import com.annimon.ownlang.parser.ast.Statement;
public class ExecutionStage implements Stage<Statement, Statement> {
@Override
public Statement perform(StagesData stagesData, Statement input) {
input.execute();
return input;
}
}

View File

@ -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<Statement, Statement> {
@Override
public Statement perform(StagesData stagesData, Statement input) {
input.accept(new FunctionAdder());
return input;
}
}

View File

@ -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<String, List<Token>> {
public static final String TAG_TOKENS = "tokens";
@Override
public List<Token> perform(StagesData stagesData, String input) {
Lexer lexer = new Lexer(input);
List<Token> tokens = lexer.tokenize();
stagesData.put(TAG_TOKENS, tokens);
return tokens;
}
}

View File

@ -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<Statement, Statement> {
@Override
public Statement perform(StagesData stagesData, Statement input) {
Linter.lint(input);
return input;
}
}

View File

@ -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<Statement, Statement> {
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);
}
}

View File

@ -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<List<Token>, Statement> {
public static final String TAG_PROGRAM = "program";
public static final String TAG_HAS_PARSE_ERRORS = "hasParseErrors";
@Override
public Statement perform(StagesData stagesData, List<Token> 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;
}
}

View File

@ -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<String, String> {
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);
}
}
}

View File

@ -1,7 +1,7 @@
package com.annimon.ownlang.utils; package com.annimon.ownlang.utils;
import com.annimon.ownlang.Console; import com.annimon.ownlang.Console;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -10,8 +10,8 @@ public final class TimeMeasurement {
private final Map<String, Long> finished, running; private final Map<String, Long> finished, running;
public TimeMeasurement() { public TimeMeasurement() {
finished = new HashMap<>(); finished = new LinkedHashMap<>();
running = new HashMap<>(); running = new LinkedHashMap<>();
} }
public void clear() { public void clear() {
@ -19,29 +19,20 @@ public final class TimeMeasurement {
running.clear(); running.clear();
} }
public void start(String... names) { public void start(String name) {
final long time = System.nanoTime(); running.put(name, System.nanoTime());
for (String name : names) { }
running.put(name, time);
public void pause(String name) {
if (running.containsKey(name)) {
addTime(name, System.nanoTime() - running.get(name));
running.remove(name);
} }
} }
public void pause(String... names) { public void stop(String name) {
final long time = System.nanoTime(); if (running.containsKey(name)) {
for (String name : names) { addTime(name, System.nanoTime() - running.get(name));
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));
}
} }
} }