Skip to content

Commit

Permalink
feat: sleep keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
Eddie authored and Eddie committed Jun 10, 2024
1 parent ba3d55a commit eb5621e
Showing 10 changed files with 128 additions and 15 deletions.
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -174,22 +174,24 @@ Ensure the class for `multitask`:
- implements the `java.lang.Runnable` interface
- 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
---

This program demonstrates:
- Multi-threading is possible with this language
- Threads print to the same standard output
- Runnable classes are compiled from a single program

# 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 jungle classpath is used to declare dependencies to compile the program and run the program.
export JUNGLE_CLASSPATH='.:./programs/classes'
The program describes 2 threads:
1. The first thread pauses program execution for a short duration and then prints out `thread!`
2. The second thread immediately prints out `Inline...`

# Compile and run the program
cat programs/multitask.source | jungle run
```shell
cat programs/multitask-inline.source | jungle run
```

Expected output:
```
Hello, world!
Inline... thread!
```

## Environment Variables
Binary file modified out/artifacts/jungle_jar/jungle.jar
Binary file not shown.
6 changes: 1 addition & 5 deletions programs/multitask-inline.source
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
multitask "com.example.RunnableTaskOne" : {
# Wait a little
x = 999999
loop (greaterThan x 0) {
x = - x 1
}
sleep 100
print "thread!\n"
}

11 changes: 11 additions & 0 deletions programs/multitask.source
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
# Compile the Java class used by our program
# `javac programs/classes/com/example/MultitaskRunnable.java`

# 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 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`

multitask "com.example.MultitaskRunnable"
print "Hello"
1 change: 1 addition & 0 deletions programs/sleep.source
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sleep 1000
1 change: 1 addition & 0 deletions src/main/java/com/jungle/ast/NodeType.java
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ public enum NodeType {
ASSERT,
PRINT,
MULTITASK,
SLEEP,

TRUE,
FALSE;
16 changes: 16 additions & 0 deletions src/main/java/com/jungle/compiler/visitor/MainVisitor.java
Original file line number Diff line number Diff line change
@@ -102,6 +102,17 @@ private MultitaskVisitor getMultitaskVisitor() {
return multitaskVisitor;
}

@Nullable
private SleepVisitor sleepVisitor;

@NotNull
private SleepVisitor getSleepVisitor() {
if (sleepVisitor == null) {
sleepVisitor = new SleepVisitor(getCompilerOptions());
}
return sleepVisitor;
}

@Nullable
private SequenceVisitor sequenceVisitor;

@@ -180,6 +191,11 @@ public void visit(
return;
}

if (getSleepVisitor().canVisit(ast)) {
getSleepVisitor().visit(mv, ast, context);
return;
}

throw new Error("unexpected node " + ast);
}
}
66 changes: 66 additions & 0 deletions src/main/java/com/jungle/compiler/visitor/SleepVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.jungle.compiler.visitor;

import static org.objectweb.asm.Opcodes.I2L;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;

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

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;

public class SleepVisitor extends AbstractVisitor {

@Nullable
private ExpressionVisitor expressionVisitor;

@NotNull
private ExpressionVisitor getExpressionVisitor() {
if (expressionVisitor == null) {
expressionVisitor = new ExpressionVisitor(getCompilerOptions());
}
return expressionVisitor;
}

public SleepVisitor(@NotNull ICompilerOptions options) {
super(options);
}

@Override
public boolean canVisit(@NotNull INode ast) {
return ast.getType().equals(NodeType.SLEEP);
}

@Override
public void visit(@NotNull MethodVisitor mv, @NotNull INode ast, @NotNull OperandStackContext context) {
// generate code for the integer representing the number of milliseconds to wait

if (ast.getLeft() == null) {
throw new Error("sleep missing expression");
}

// push expression onto operand stack
getExpressionVisitor().visit(mv, ast.getLeft(), context);

// verify that the expression was an integer type
if (context.peek() != OperandStackType.INTEGER) {
throw new Error("sleep expression expected to be type integer");
}

// For now, convert the integer to long
mv.visitInsn(I2L);

// invoke static method: Thread.sleep()
mv.visitMethodInsn(
INVOKESTATIC,
"java/lang/Thread",
"sleep",
"(J)V",
false
);
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/jungle/parser/Parser.java
Original file line number Diff line number Diff line change
@@ -81,11 +81,15 @@ protected INode parseNumber() {
public INode parseNumberExpression() {
/*
* number_expression := number
* | identifier
* | "(" number_expression ")"
* | ( "+" | "-" | "*" | "/" | "%" ) expression expression
* ;
*/
consumeWhitespace();
if (accepts(TokenType.SYMBOL)) {
return parseIdentifier();
}
if (accepts(TokenType.NUMBER)) {
return parseNumber();
}
@@ -362,6 +366,16 @@ protected INode parseStatementMultitask() {
return node;
}

@NotNull
protected INode parseStatementSleep() {
/*
* statement_sleep := "sleep" expression_number
*/
expectKeyword(KEYWORD_SLEEP);
consumeWhitespace();
return new Node(NodeType.SLEEP).withLeft(parseNumberExpression());
}

@Nullable
protected INode parseStatementIf() {
/*
@@ -418,6 +432,7 @@ protected INode parseStatementKeyword() {
* | statement_print
* | statement_if
* | statement_multitask
* | statement_wait
* ;
*/
consumeWhitespace();
@@ -436,6 +451,9 @@ protected INode parseStatementKeyword() {
if (acceptKeyword(KEYWORD_MULTITASK)) {
return parseStatementMultitask();
}
if (acceptKeyword(KEYWORD_SLEEP)) {
return parseStatementSleep();
}
throw newError("not a keyword statement");
}

2 changes: 2 additions & 0 deletions src/main/java/com/jungle/scanner/Scanner.java
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ public class Scanner extends AbstractScanner {
public static final String KEYWORD_LOOP = "loop";
public static final String KEYWORD_PRINT = "print";
public static final String KEYWORD_MULTITASK = "multitask";
public static final String KEYWORD_SLEEP = "sleep";
public static final String KEYWORD_AND = "and";
public static final String KEYWORD_OR = "or";
public static final String KEYWORD_NOT = "not";
@@ -35,6 +36,7 @@ public class Scanner extends AbstractScanner {
KEYWORD_LOOP,
KEYWORD_PRINT,
KEYWORD_MULTITASK,
KEYWORD_SLEEP,
KEYWORD_AND,
KEYWORD_OR,
KEYWORD_NOT,

0 comments on commit eb5621e

Please sign in to comment.