Skip to content

Commit

Permalink
feat: compile target path
Browse files Browse the repository at this point in the history
  • Loading branch information
Eddie authored and Eddie committed Jun 10, 2024
1 parent 91ac078 commit ba3d55a
Show file tree
Hide file tree
Showing 22 changed files with 199 additions and 120 deletions.
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A toy programming language built for the Java Virtual Machine.
## TODO

- casting: integer, float, character
- compile a runnable class
- sleep

## Features

Expand All @@ -23,8 +23,8 @@ Most features are inherited from JVM.
This project is for educational purposes (but mostly to have fun). There are a couple of goals for this language.

- JVM programming accessibility for command-line
- fully transparent compiling process
- simplified Java language feature
- Fully transparent compiling process
- Simplified Java language feature
- no object oriented programming
- no `null`

Expand Down Expand Up @@ -172,16 +172,16 @@ To use this keyword, supply a `*.class` file/binary path (excluding extension).
Ensure the class for `multitask`:
- has a default constructor
- implements the `java.lang.Runnable` interface
- compiled with the `JUNGLEPATH` environment variable with the classpath
- compiled with the `JUNGLE_CLASSPATH` environment variable with the classpath

```shell
# Compile the Java class used by our program
javac programs/classes/com/example/MultitaskRunnable.java

# Declare or provide the junglepath before compiling or running,
# Declare or provide the jungle classpath before compiling or running,
# so that our Runnable class can be validated during compile and found during runtime.
# The junglepath is an isolated classpath for the just the program.
export JUNGLEPATH='.:./programs/classes'
# The jungle classpath is used to declare dependencies to compile the program and run the program.
export JUNGLE_CLASSPATH='.:./programs/classes'

# Compile and run the program
cat programs/multitask.source | jungle run
Expand All @@ -192,6 +192,13 @@ Expected output:
Hello, world!
```

## Environment Variables

| Name | Default | For Compile | For Runtime | Description |
|---------------------|---------|-------------|-------------|---|
| `JUNGLE_CLASSPATH` | `.` | yes | yes | Similar to the JVM `CLASSPATH`. Used to specify class dependencies required for compiling and runtime of the program. |
| `JUNGLE_TARGETPATH` | `.` | yes | no | Used to specify a target output folder for classes generated when compiling. |

## Compile the Compiler

1. Load project using VS Code
Expand Down
Binary file modified out/artifacts/jungle_jar/jungle.jar
Binary file not shown.
53 changes: 36 additions & 17 deletions src/main/java/com/jungle/JungleCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,28 @@ protected static BufferedWriter getBufferedWriter(@NotNull CommandLine cli) thro
}
}

public static String DEFAULT_JUNGLEPATH = ".";
public static String DEFAULT_JUNGLE_CLASSPATH = ".";

@NotNull
protected static String getJunglePath(@NotNull CommandLine cli) {
/* The junglepath should be independent of classpath,
* since the dependencies of the compiler should be isolated from the program
*/
String cliValue = cli.getOptionValue("junglepath");
protected static String getJungleClassPath(@NotNull CommandLine cli) {
/* The jungle classpath is used to declare dependencies of the program. */
String cliValue = cli.getOptionValue("classpath");
if (cliValue != null) return cliValue;
String environmentValue = System.getenv("JUNGLE_CLASSPATH");
if (environmentValue != null) return environmentValue;
return DEFAULT_JUNGLE_CLASSPATH;
}

public static String DEFAULT_JUNGLE_TARGETPATH = ".";

@NotNull
protected static String getJungleTargetPath(@NotNull CommandLine cli) {
/* The jungle targetpath is used to specify an output path of the program class files. */
String cliValue = cli.getOptionValue("targetpath");
if (cliValue != null) return cliValue;
String environmentValue = System.getenv("JUNGLEPATH");
String environmentValue = System.getenv("JUNGLE_TARGETPATH");
if (environmentValue != null) return environmentValue;
return DEFAULT_JUNGLEPATH;
return DEFAULT_JUNGLE_TARGETPATH;
}

protected static void helpCommand(@NotNull Options options) {
Expand Down Expand Up @@ -120,10 +130,14 @@ protected static void compileCommand(@NotNull CommandLine cli) {
System.err.println("failed to close ast reader - " + e.getMessage());
System.exit(1);
}
String outputFileName = cli.getOptionValue("output", "Entrypoint");
String junglePath = getJunglePath(cli);
Compiler compiler = new Compiler();
compiler.compileMain(outputFileName, new MainVisitor(junglePath), ast);
String entrypointClassName = cli.getOptionValue("output", "Entrypoint");
String jungleClassPath = getJungleClassPath(cli);
String jungleTargetPath = getJungleTargetPath(cli);
Compiler compiler = new Compiler(
jungleClassPath,
jungleTargetPath
);
compiler.compileMain(entrypointClassName, new MainVisitor(compiler), ast);
}

protected static void runCommand(@NotNull CommandLine cli) {
Expand All @@ -137,9 +151,13 @@ protected static void runCommand(@NotNull CommandLine cli) {
INode ast = parser.parse();
// Compile...
String entrypointClassName = cli.getOptionValue("output", "Entrypoint");
String junglePath = getJunglePath(cli);
Compiler compiler = new Compiler();
compiler.compileMain(entrypointClassName, new MainVisitor(junglePath), ast);
String jungleClassPath = getJungleClassPath(cli);
String jungleTargetPath = getJungleTargetPath(cli);
Compiler compiler = new Compiler(
jungleClassPath,
jungleTargetPath
);
compiler.compileMain(entrypointClassName, new MainVisitor(compiler), ast);
// Run...
/* Problem:
* Loading the new class using reflection and invoking main appears to work in some cases.
Expand All @@ -152,7 +170,7 @@ protected static void runCommand(@NotNull CommandLine cli) {

String[] command = {
"java",
"-classpath", getJunglePath(cli),
"-classpath", jungleClassPath,
entrypointClassName
};
Process process;
Expand All @@ -177,7 +195,8 @@ public static void main(String[] args) {
options.addOption("h", "help", false, "Show help options.");
options.addOption("k", "keywords", false, "Show keywords.");
options.addOption("o", "output", true, "Output file name.");
options.addOption("p", "junglepath", true, "Class path of the jungle program.");
options.addOption("c", "classpath", true, "Class path of the program - source of the program class file dependencies.");
options.addOption("t", "targetpath", true, "Target path of the program - destination of the program class files generated.");

CommandLineParser cliParser = new DefaultParser();
CommandLine cli = null;
Expand Down
48 changes: 41 additions & 7 deletions src/main/java/com/jungle/compiler/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.jungle.error.CompilerError;
import com.jungle.logger.FileLogger;
import com.jungle.compiler.visitor.*;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.ClassReader;
Expand All @@ -17,32 +18,65 @@

import static org.objectweb.asm.Opcodes.*;

public class Compiler {
public class Compiler implements ICompilerOptions {
@NotNull
private static final FileLogger logger = new FileLogger(Compiler.class.getSimpleName());

@NotNull
private final String classPath;

@Override
@NotNull
public String getClassPath() {
return classPath;
}

@NotNull
private final String targetPath;

@Override
@NotNull
public String getTargetPath() {
return targetPath;
}

public Compiler(
@NotNull String classPath,
@NotNull String targetPath
) {
super();
this.classPath = classPath;
this.targetPath = targetPath;
}

public Compiler(@NotNull ICompilerOptions options) {
super();
this.classPath = options.getClassPath();
this.targetPath = options.getTargetPath();
}

// region Helpers

@NotNull
public static String asInternalClassName(@NotNull String className) {
return className.replace('.', '/');
}

public static void writeClassFile(@NotNull String fullClassName, byte[] classData) {
protected void writeClassFile(@NotNull String className, byte[] classData) {
// TODO: new class files should be placed in a target directory
// ...
String classFileString = fullClassName.replace('.', '/') + ".class";
logger.debug(String.format("write class file - %s", classFileString));
String classFileString = getTargetPath() + '/' + className.replace('.', '/') + ".class";
File classFile = new File(classFileString);
logger.debug(String.format("write class file - %s", classFile.toString()));
// Create necessary parent directories for class file
File classParentFile = classFile.getParentFile();
if (classParentFile != null) {
// Ensure folder path exists
classParentFile.mkdir();
classParentFile.mkdirs();
}
try {
boolean hasCreatedNewFile = classFile.createNewFile();
if (!hasCreatedNewFile) {
logger.debug(String.format("class already exists - %s", fullClassName));
logger.debug(String.format("class already exists - %s", className));
}
} catch (IOException e) {
String message = "failed to create class file";
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/jungle/compiler/ICompilerOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.jungle.compiler;

import org.jetbrains.annotations.NotNull;

public interface ICompilerOptions {
@NotNull
String getClassPath();

@NotNull
String getTargetPath();
}

This file was deleted.

19 changes: 19 additions & 0 deletions src/main/java/com/jungle/compiler/visitor/AbstractVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.jungle.compiler.visitor;

import org.jetbrains.annotations.NotNull;

import com.jungle.compiler.ICompilerOptions;

public abstract class AbstractVisitor implements IVisitor {
@NotNull
private final ICompilerOptions options;

public ICompilerOptions getCompilerOptions() {
return options;
}

public AbstractVisitor(@NotNull ICompilerOptions options) {
super();
this.options = options;
}
}
9 changes: 5 additions & 4 deletions src/main/java/com/jungle/compiler/visitor/AssertVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.jungle.ast.INode;
import com.jungle.ast.NodeType;
import com.jungle.compiler.ICompilerOptions;
import com.jungle.compiler.operand.OperandStackContext;
import com.jungle.compiler.operand.OperandStackType;
import com.jungle.logger.FileLogger;
Expand All @@ -12,7 +13,7 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class AssertVisitor extends AbstractClassPathVisitor {
public class AssertVisitor extends AbstractVisitor {
@NotNull
private static final FileLogger logger = new FileLogger(AssertVisitor.class.getName());

Expand All @@ -27,13 +28,13 @@ public class AssertVisitor extends AbstractClassPathVisitor {
@NotNull
private ExpressionVisitor getExpressionVisitor() {
if (expressionVisitor == null) {
expressionVisitor = new ExpressionVisitor(getClassPath());
expressionVisitor = new ExpressionVisitor(getCompilerOptions());
}
return expressionVisitor;
}

public AssertVisitor(@NotNull final String classPath) {
super(classPath);
public AssertVisitor(@NotNull ICompilerOptions options) {
super(options);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import com.jungle.ast.INode;
import com.jungle.ast.NodeType;
import com.jungle.compiler.ICompilerOptions;
import com.jungle.compiler.operand.OperandStackContext;
import com.jungle.logger.FileLogger;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.MethodVisitor;

public class AssignmentVisitor extends AbstractClassPathVisitor {
public class AssignmentVisitor extends AbstractVisitor {
@NotNull
private static final FileLogger logger = new FileLogger(AssignmentVisitor.class.getName());

Expand All @@ -19,13 +20,13 @@ public class AssignmentVisitor extends AbstractClassPathVisitor {
@NotNull
private ExpressionVisitor getExpressionVisitor() {
if (expressionVisitor == null) {
expressionVisitor = new ExpressionVisitor(getClassPath());
expressionVisitor = new ExpressionVisitor(getCompilerOptions());
}
return expressionVisitor;
}

public AssignmentVisitor(@NotNull final String classPath) {
super(classPath);
public AssignmentVisitor(@NotNull ICompilerOptions options) {
super(options);
}

@Override
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/com/jungle/compiler/visitor/BlockVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import com.jungle.ast.INode;
import com.jungle.ast.NodeType;
import com.jungle.compiler.ICompilerOptions;
import com.jungle.compiler.operand.OperandStackContext;
import com.jungle.logger.FileLogger;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.MethodVisitor;

public class BlockVisitor extends AbstractClassPathVisitor {
public class BlockVisitor extends AbstractVisitor {
@NotNull
private static final FileLogger logger = new FileLogger(BlockVisitor.class.getName());

Expand All @@ -18,13 +19,13 @@ public class BlockVisitor extends AbstractClassPathVisitor {

private MainVisitor getMainVisitor() {
if (mainVisitor == null) {
mainVisitor = new MainVisitor(getClassPath());
mainVisitor = new MainVisitor(getCompilerOptions());
}
return mainVisitor;
}

public BlockVisitor(@NotNull final String classPath) {
super(classPath);
public BlockVisitor(@NotNull ICompilerOptions options) {
super(options);
}

@Override
Expand Down
Loading

0 comments on commit ba3d55a

Please sign in to comment.