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;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user