mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Introduce stages for each main feature
This commit is contained in:
parent
ea38227b44
commit
0d820c8a91
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.annimon.ownlang.stages;
|
||||
|
||||
public interface StagesData {
|
||||
|
||||
<T> T get(String tag);
|
||||
|
||||
void put(String tag, Object input);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -142,55 +137,41 @@ public final class Main {
|
||||
}
|
||||
|
||||
private static void run(String input, RunOptions options) {
|
||||
options.validate();
|
||||
final TimeMeasurement measurement = new TimeMeasurement();
|
||||
measurement.start("Tokenize time");
|
||||
final List<Token> 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));
|
||||
}
|
||||
}
|
||||
final var measurement = new TimeMeasurement();
|
||||
final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop);
|
||||
|
||||
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 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<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(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String, Long> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user