Skip to content

Commit c5810f0

Browse files
committed
Add input sources and source loading stage
1 parent 5dcf31c commit c5810f0

File tree

10 files changed

+202
-90
lines changed

10 files changed

+202
-90
lines changed

ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
import com.annimon.ownlang.Console;
44
import com.annimon.ownlang.stages.Stage;
55
import com.annimon.ownlang.stages.StagesData;
6+
import com.annimon.ownlang.util.input.SourceLoaderStage;
67

78
public class ErrorsLocationFormatterStage implements Stage<Iterable<? extends SourceLocatedError>, String> {
89

910
@Override
1011
public String perform(StagesData stagesData, Iterable<? extends SourceLocatedError> input) {
1112
final var sb = new StringBuilder();
12-
final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE);
13+
final String source = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE, "");
1314
final var lines = source.split("\r?\n");
1415
for (SourceLocatedError error : input) {
1516
sb.append(Console.newline());
1617
sb.append(error);
1718
sb.append(Console.newline());
1819
final Range range = error.getRange();
19-
if (range != null) {
20+
if (range != null && lines.length > 0) {
2021
printPosition(sb, range.normalize(), lines);
2122
}
2223
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.annimon.ownlang.util.input;
2+
3+
import java.io.IOException;
4+
5+
public interface InputSource {
6+
String getPath();
7+
8+
String load() throws IOException;
9+
10+
default String getBasePath() {
11+
int i = getPath().lastIndexOf("/");
12+
if (i == -1) return "";
13+
return getPath().substring(0, i + 1);
14+
}
15+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.annimon.ownlang.util.input;
2+
3+
import java.io.FileInputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
9+
public record InputSourceFile(String path) implements InputSource {
10+
11+
@Override
12+
public String getPath() {
13+
return path;
14+
}
15+
16+
@Override
17+
public String load() throws IOException {
18+
if (Files.isReadable(Path.of(path))) {
19+
try (InputStream is = new FileInputStream(path)) {
20+
return SourceLoaderStage.readStream(is);
21+
}
22+
}
23+
throw new IOException(path + " not found");
24+
}
25+
26+
@Override
27+
public String toString() {
28+
return "File " + path;
29+
}
30+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.annimon.ownlang.util.input;
2+
3+
public record InputSourceProgram(String program) implements InputSource {
4+
5+
@Override
6+
public String getPath() {
7+
return ".";
8+
}
9+
10+
@Override
11+
public String load() {
12+
return program;
13+
}
14+
15+
@Override
16+
public String toString() {
17+
return "Program";
18+
}
19+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.annimon.ownlang.util.input;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
6+
public record InputSourceResource(String path) implements InputSource {
7+
8+
@Override
9+
public String getPath() {
10+
return path;
11+
}
12+
13+
@Override
14+
public String load() throws IOException {
15+
try (InputStream is = getClass().getResourceAsStream(path)) {
16+
if (is != null) {
17+
return SourceLoaderStage.readStream(is);
18+
}
19+
}
20+
throw new IOException(path + " not found");
21+
}
22+
23+
@Override
24+
public String toString() {
25+
return "Resource " + path;
26+
}
27+
}

ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java renamed to ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,25 @@
1-
package com.annimon.ownlang.util;
1+
package com.annimon.ownlang.util.input;
22

33
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
44
import com.annimon.ownlang.stages.Stage;
55
import com.annimon.ownlang.stages.StagesData;
66
import java.io.ByteArrayOutputStream;
7-
import java.io.FileInputStream;
87
import java.io.IOException;
98
import java.io.InputStream;
109
import java.nio.charset.StandardCharsets;
1110

12-
public class SourceLoaderStage implements Stage<String, String> {
11+
public class SourceLoaderStage implements Stage<InputSource, String> {
1312

1413
public static final String TAG_SOURCE = "source";
1514

1615
@Override
17-
public String perform(StagesData stagesData, String name) {
16+
public String perform(StagesData stagesData, InputSource inputSource) {
1817
try {
19-
String result = readSource(name);
18+
String result = inputSource.load();
2019
stagesData.put(TAG_SOURCE, result);
2120
return result;
2221
} catch (IOException e) {
23-
throw new OwnLangRuntimeException("Unable to read input " + name, e);
24-
}
25-
}
26-
27-
private String readSource(String name) throws IOException {
28-
try (InputStream is = getClass().getResourceAsStream("/" + name)) {
29-
if (is != null) {
30-
return readStream(is);
31-
}
32-
}
33-
try (InputStream is = new FileInputStream(name)) {
34-
return readStream(is);
22+
throw new OwnLangRuntimeException("Unable to read input " + inputSource, e);
3523
}
3624
}
3725

ownlang-desktop/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,3 @@ tasks.register('runOptimizationDumper', JavaExec) {
5656
classpath = sourceSets.main.runtimeClasspath
5757
args '../program.own'
5858
}
59-
//

ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java

Lines changed: 37 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
import com.annimon.ownlang.exceptions.OwnLangParserException;
44
import com.annimon.ownlang.exceptions.StoppedException;
55
import com.annimon.ownlang.parser.Beautifier;
6-
import com.annimon.ownlang.parser.SourceLoader;
76
import com.annimon.ownlang.parser.Token;
87
import com.annimon.ownlang.parser.ast.Statement;
98
import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage;
109
import com.annimon.ownlang.parser.linters.LinterStage;
1110
import com.annimon.ownlang.parser.optimization.OptimizationStage;
1211
import com.annimon.ownlang.stages.*;
12+
import com.annimon.ownlang.util.input.SourceLoaderStage;
1313
import com.annimon.ownlang.utils.Repl;
1414
import com.annimon.ownlang.utils.Sandbox;
1515
import com.annimon.ownlang.utils.TimeMeasurement;
@@ -23,17 +23,12 @@
2323
public final class Main {
2424

2525
public static void main(String[] args) throws IOException {
26-
if (args.length == 0) {
27-
try {
28-
runDefault();
29-
} catch (IOException ioe) {
30-
printUsage();
31-
}
26+
final RunOptions options = new RunOptions();
27+
if (args.length == 0 && (options.detectDefaultProgramPath() == null)) {
28+
printUsage();
3229
return;
3330
}
3431

35-
final RunOptions options = new RunOptions();
36-
String input = null;
3732
for (int i = 0; i < args.length; i++) {
3833
switch (args[i]) {
3934
case "-a":
@@ -82,73 +77,69 @@ public static void main(String[] args) throws IOException {
8277

8378
case "-f":
8479
case "--file":
85-
if (i + 1 < args.length) {
86-
input = SourceLoader.readSource(args[i + 1]);
80+
if (i + 1 < args.length) {
81+
options.programPath = args[i + 1];
8782
createOwnLangArgs(args, i + 2);
8883
i++;
8984
}
9085
break;
9186

9287
case "--sandbox":
93-
createOwnLangArgs(args, i + 1);
94-
final String[] ownlangArgs = Shared.getOwnlangArgs();
95-
String[] newArgs = new String[ownlangArgs.length];
96-
System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length);
97-
Sandbox.main(newArgs);
88+
Sandbox.main(createOwnLangArgs(args, i + 1));
9889
return;
9990

10091
default:
101-
if (input == null) {
102-
input = args[i];
92+
if (options.programSource == null) {
93+
options.programSource = args[i];
10394
createOwnLangArgs(args, i + 1);
10495
}
10596
break;
10697
}
10798
}
108-
if (input == null) {
109-
throw new IllegalArgumentException("Empty input");
110-
}
11199
if (options.beautifyMode) {
100+
String input = new SourceLoaderStage()
101+
.perform(new StagesDataMap(), options.toInputSource());
112102
System.out.println(Beautifier.beautify(input));
113103
return;
114104
}
115-
run(input, options);
116-
}
117-
118-
private static void runDefault() throws IOException {
119-
final RunOptions options = new RunOptions();
120-
run(SourceLoader.readSource("program.own"), options);
105+
run(options);
121106
}
122107

123108
private static void printUsage() {
124-
System.out.println("OwnLang version " + Version.VERSION + "\n\n" +
125-
"Usage: ownlang [options]\n" +
126-
" options:\n" +
127-
" -f, --file [input] Run program file. Required.\n" +
128-
" -r, --repl Enter to a REPL mode\n" +
129-
" -l, --lint Find bugs in code\n" +
130-
" -o N, --optimize N Perform optimization with N passes\n" +
131-
" -b, --beautify Beautify source code\n" +
132-
" -a, --showast Show AST of program\n" +
133-
" -t, --showtokens Show lexical tokens\n" +
134-
" -m, --showtime Show elapsed time of parsing and execution");
109+
System.out.println("OwnLang version %s\n\n".formatted(Version.VERSION) + """
110+
Usage: ownlang [options]
111+
options:
112+
-f, --file [input] Run program file. Required.
113+
-r, --repl Enter to a REPL mode
114+
-l, --lint Find bugs in code
115+
-o N, --optimize N Perform optimization with N (0...9) passes
116+
-b, --beautify Beautify source code
117+
-a, --showast Show AST of program
118+
-t, --showtokens Show lexical tokens
119+
-m, --showtime Show elapsed time of parsing and execution
120+
""");
135121
}
136122

137-
private static void createOwnLangArgs(String[] javaArgs, int index) {
138-
if (index >= javaArgs.length) return;
139-
final String[] ownlangArgs = new String[javaArgs.length - index];
140-
System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length);
123+
private static String[] createOwnLangArgs(String[] javaArgs, int index) {
124+
final String[] ownlangArgs;
125+
if (index >= javaArgs.length) {
126+
ownlangArgs = new String[0];
127+
} else {
128+
ownlangArgs = new String[javaArgs.length - index];
129+
System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length);
130+
}
141131
Shared.setOwnlangArgs(ownlangArgs);
132+
return ownlangArgs;
142133
}
143134

144-
private static void run(String input, RunOptions options) {
135+
private static void run(RunOptions options) {
145136
final var measurement = new TimeMeasurement();
146137
final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop);
147138

148139
final var stagesData = new StagesDataMap();
149-
stagesData.put(SourceLoaderStage.TAG_SOURCE, input);
150140
try {
151-
scopedStages.create("Lexer", new LexerStage())
141+
scopedStages.create("Source loader", new SourceLoaderStage())
142+
.then(scopedStages.create("Lexer", new LexerStage()))
152143
.then(scopedStages.create("Parser", new ParserStage()))
153144
.thenConditional(options.optimizationLevel > 0,
154145
scopedStages.create("Optimization",
@@ -157,7 +148,7 @@ private static void run(String input, RunOptions options) {
157148
scopedStages.create("Linter", new LinterStage()))
158149
.then(scopedStages.create("Function adding", new FunctionAddingStage()))
159150
.then(scopedStages.create("Execution", new ExecutionStage()))
160-
.perform(stagesData, input);
151+
.perform(stagesData, options.toInputSource());
161152
} catch (OwnLangParserException ex) {
162153
final var error = new ParseErrorsFormatterStage()
163154
.perform(stagesData, ex.getParseErrors());
@@ -185,20 +176,4 @@ private static void run(String input, RunOptions options) {
185176
}
186177
}
187178
}
188-
189-
private static class RunOptions {
190-
boolean showTokens, showAst, showMeasurements;
191-
boolean lintMode;
192-
boolean beautifyMode;
193-
int optimizationLevel;
194-
195-
RunOptions() {
196-
showTokens = false;
197-
showAst = false;
198-
showMeasurements = false;
199-
lintMode = false;
200-
beautifyMode = false;
201-
optimizationLevel = 0;
202-
}
203-
}
204179
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.annimon.ownlang;
2+
3+
import com.annimon.ownlang.util.input.InputSource;
4+
import com.annimon.ownlang.util.input.InputSourceFile;
5+
import com.annimon.ownlang.util.input.InputSourceProgram;
6+
import com.annimon.ownlang.util.input.InputSourceResource;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
10+
public class RunOptions {
11+
private static final String RESOURCE_PREFIX = "resource:";
12+
private static final String DEFAULT_PROGRAM = "program.own";
13+
14+
// input
15+
String programPath;
16+
String programSource;
17+
// modes
18+
boolean lintMode;
19+
boolean beautifyMode;
20+
int optimizationLevel;
21+
// flags
22+
boolean showTokens;
23+
boolean showAst;
24+
boolean showMeasurements;
25+
26+
String detectDefaultProgramPath() {
27+
if (getClass().getResource("/" + DEFAULT_PROGRAM) != null) {
28+
return RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM;
29+
}
30+
if (Files.isReadable(Path.of(DEFAULT_PROGRAM))) {
31+
return DEFAULT_PROGRAM;
32+
}
33+
return null;
34+
}
35+
36+
InputSource toInputSource() {
37+
if (programSource != null) {
38+
return new InputSourceProgram(programSource);
39+
}
40+
if (programPath == null) {
41+
// No arguments. Default to program.own
42+
programPath = detectDefaultProgramPath();
43+
if (programPath == null) {
44+
throw new IllegalArgumentException("Empty input");
45+
}
46+
}
47+
48+
if (programPath.startsWith(RESOURCE_PREFIX)) {
49+
String path = programPath.substring(RESOURCE_PREFIX.length());
50+
return new InputSourceResource(path);
51+
} else {
52+
return new InputSourceFile(programPath);
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)