Skip to content

Commit

Permalink
add a class to control the dynamic variables
Browse files Browse the repository at this point in the history
  • Loading branch information
fglock committed Oct 22, 2024
1 parent ddf668e commit b33de15
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 23 deletions.
6 changes: 4 additions & 2 deletions src/main/java/org/perlonjava/codegen/EmitLiteral.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,14 @@ static void emitList(EmitterVisitor emitterVisitor, ListNode node) {
mv.visitInsn(Opcodes.DUP);
// stack: [RuntimeList] [RuntimeList]

emitterVisitor.ctx.javaClassInfo.incrementStackLevel(2);;
emitterVisitor.ctx.javaClassInfo.incrementStackLevel(2);
;

// emit the list element
element.accept(emitterVisitor.with(RuntimeContextType.LIST));

emitterVisitor.ctx.javaClassInfo.decrementStackLevel(2);;
emitterVisitor.ctx.javaClassInfo.decrementStackLevel(2);
;

// Call the add method to add the element to the RuntimeList
// This calls RuntimeDataProvider.addToList() in order to allow (1, 2, $x, @x, %x)
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/perlonjava/codegen/EmitStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) {
mv.visitInsn(Opcodes.POP); // we don't need the variable in the stack
// Stack: [iterator]

emitterVisitor.ctx.javaClassInfo.incrementStackLevel(1);;
emitterVisitor.ctx.javaClassInfo.incrementStackLevel(1);
;

// Add redo label
Label redoLabel = new Label();
Expand Down Expand Up @@ -226,7 +227,8 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) {
// End of the loop
mv.visitLabel(loopEnd);

emitterVisitor.ctx.javaClassInfo.decrementStackLevel(1);;
emitterVisitor.ctx.javaClassInfo.decrementStackLevel(1);
;

// Pop the iterator from the stack
mv.visitInsn(Opcodes.POP);
Expand Down
16 changes: 12 additions & 4 deletions src/main/java/org/perlonjava/codegen/JavaClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@
*/
public class JavaClassInfo {

/** The name of the Java class. */
/**
* The name of the Java class.
*/
public String javaClassName;

/** The label to return to after method execution. */
/**
* The label to return to after method execution.
*/
public Label returnLabel;

/** Manages the stack level for the class. */
/**
* Manages the stack level for the class.
*/
public StackLevelManager stackLevelManager;

/** A stack of loop labels for managing nested loops. */
/**
* A stack of loop labels for managing nested loops.
*/
public Deque<LoopLabels> loopLabelStack;

/**
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/org/perlonjava/parser/ListParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ListParser {
/**
* Parses a list with zero or one optional argument. The list can be enclosed
* in parentheses or not. Commas are allowed after the argument.
*
* <p>
* Examples:
* - rand()
* - rand(10)
Expand Down Expand Up @@ -66,12 +66,12 @@ static ListNode parseZeroOrOneList(Parser parser, int minItems) {
* parentheses or not. Supports various parsing contexts such as block nodes,
* file handles, and regex patterns.
*
* @param parser The parser instance.
* @param minItems The minimum number of items required in the list.
* @param wantBlockNode Indicates if a block node is expected.
* @param obeyParentheses Indicates if parentheses should be obeyed.
* @param wantFileHandle Indicates if a file handle is expected.
* @param wantRegex Indicates if a regex pattern is expected.
* @param parser The parser instance.
* @param minItems The minimum number of items required in the list.
* @param wantBlockNode Indicates if a block node is expected.
* @param obeyParentheses Indicates if parentheses should be obeyed.
* @param wantFileHandle Indicates if a file handle is expected.
* @param wantRegex Indicates if a regex pattern is expected.
* @return A ListNode representing the parsed list.
* @throws PerlCompilerException If the syntax is incorrect or the minimum number of items is not met.
*/
Expand Down Expand Up @@ -177,7 +177,7 @@ static ListNode parseZeroOrMoreList(Parser parser, int minItems, boolean wantBlo
* Parses a generic list with a specified closing delimiter. This method is
* used for parsing various constructs like parentheses, hash literals, and
* array literals.
*
* <p>
* Example usage:
* - new ListNode(parseList(")", 0), tokenIndex);
* - new HashLiteralNode(parseList("}", 1), tokenIndex);
Expand Down
48 changes: 47 additions & 1 deletion src/main/java/org/perlonjava/runtime/RuntimeArray.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.perlonjava.runtime;

import org.perlonjava.codegen.DynamicState;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import static org.perlonjava.runtime.RuntimeScalarCache.getScalarInt;

Expand All @@ -12,7 +15,9 @@
* <p>In Perl, an array is a dynamic list of scalar values. This class tries to mimic this behavior
* using a list of RuntimeScalar objects, which can hold any type of Perl scalar value.
*/
public class RuntimeArray extends RuntimeBaseEntity implements RuntimeScalarReference {
public class RuntimeArray extends RuntimeBaseEntity implements RuntimeScalarReference, DynamicState {
// Static stack to store saved "local" states of RuntimeArray instances
private static final Stack<RuntimeArray> dynamicStateStack = new Stack<>();
// List to hold the elements of the array.
public List<RuntimeScalar> elements;

Expand Down Expand Up @@ -408,6 +413,47 @@ public String toString() {
return sb.toString();
}

/**
* Saves the current state of the RuntimeArray instance.
*
* <p>This method creates a snapshot of the current elements and blessId of the array,
* and pushes it onto a static stack for later restoration. After saving, it clears
* the current elements and resets the blessId.
*/
@Override
public void dynamicSaveState() {
// Create a new RuntimeArray to save the current state
RuntimeArray currentState = new RuntimeArray();
// Copy the current elements to the new state
currentState.elements = new ArrayList<>(this.elements);
// Copy the current blessId to the new state
currentState.blessId = this.blessId;
// Push the current state onto the stack
dynamicStateStack.push(currentState);
// Clear the array elements
this.elements.clear();
// Reset the blessId
this.blessId = 0;
}

/**
* Restores the most recently saved state of the RuntimeArray instance.
*
* <p>This method pops the most recent state from the static stack and restores
* the elements and blessId to the current array. If no state is saved, it does nothing.
*/
@Override
public void dynamicRestoreState() {
if (!dynamicStateStack.isEmpty()) {
// Pop the most recent saved state from the stack
RuntimeArray previousState = dynamicStateStack.pop();
// Restore the elements from the saved state
this.elements = previousState.elements;
// Restore the blessId from the saved state
this.blessId = previousState.blessId;
}
}

/**
* Inner class implementing the Iterator interface for RuntimeArray.
*/
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/perlonjava/runtime/RuntimeHash.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
* any type of Perl scalar value.
*/
public class RuntimeHash extends RuntimeBaseEntity implements RuntimeScalarReference, DynamicState {
// Static stack to store saved "local" states of RuntimeHash instances
private static final Stack<RuntimeHash> dynamicStateStack = new Stack<>();
// Map to store the elements of the hash
public Map<String, RuntimeScalar> elements;
// Iterator for traversing the hash elements
Iterator<RuntimeScalar> hashIterator;

// Static stack to store saved "local" states of RuntimeHash instances
private static final Stack<RuntimeHash> dynamicStateStack = new Stack<>();

/**
* Constructor for RuntimeHash.
* Initializes an empty hash map to store elements.
Expand Down
47 changes: 44 additions & 3 deletions src/main/java/org/perlonjava/runtime/RuntimeScalar.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ibm.icu.text.CaseMap;
import com.ibm.icu.text.Normalizer2;
import org.perlonjava.ArgumentParser;
import org.perlonjava.codegen.DynamicState;
import org.perlonjava.perlmodule.Universal;
import org.perlonjava.scriptengine.PerlLanguageProvider;

Expand Down Expand Up @@ -39,7 +40,7 @@
* to mimic this behavior by using an enum `RuntimeScalarType` to track the type of the value stored in the
* scalar.
*/
public class RuntimeScalar extends RuntimeBaseEntity implements RuntimeScalarReference {
public class RuntimeScalar extends RuntimeBaseEntity implements RuntimeScalarReference, DynamicState {

private static final int MAX_NUMIFICATION_CACHE_SIZE = 1000;
private static final Map<String, RuntimeScalar> numificationCache = new LinkedHashMap<String, RuntimeScalar>(MAX_NUMIFICATION_CACHE_SIZE, 0.75f, true) {
Expand All @@ -48,10 +49,10 @@ protected boolean removeEldestEntry(Map.Entry<String, RuntimeScalar> eldest) {
return size() > MAX_NUMIFICATION_CACHE_SIZE;
}
};

// Static stack to store saved "local" states of RuntimeScalar instances
private static final Stack<RuntimeScalar> dynamicStateStack = new Stack<>();
private static long currentSeed = System.currentTimeMillis();
private static final Random random = new Random(currentSeed);

// Fields to store the type and value of the scalar variable
public RuntimeScalarType type;
public Object value;
Expand Down Expand Up @@ -1772,6 +1773,46 @@ public RuntimeIO getRuntimeIO() {
return fh;
}

/**
* Saves the current state of the RuntimeScalar instance.
*
* <p>This method creates a snapshot of the current type and value of the scalar,
* and pushes it onto a static stack for later restoration.
*/
@Override
public void dynamicSaveState() {
// Create a new RuntimeScalar to save the current state
RuntimeScalar currentState = new RuntimeScalar();
// Copy the current type and value to the new state
currentState.type = this.type;
currentState.value = this.value;
currentState.blessId = this.blessId;
// Push the current state onto the stack
dynamicStateStack.push(currentState);
// Clear the current type and value
this.type = RuntimeScalarType.UNDEF;
this.value = null;
this.blessId = 0;
}

/**
* Restores the most recently saved state of the RuntimeScalar instance.
*
* <p>This method pops the most recent state from the static stack and restores
* the type and value to the current scalar. If no state is saved, it does nothing.
*/
@Override
public void dynamicRestoreState() {
if (!dynamicStateStack.isEmpty()) {
// Pop the most recent saved state from the stack
RuntimeScalar previousState = dynamicStateStack.pop();
// Restore the type, value from the saved state
this.type = previousState.type;
this.value = previousState.value;
this.blessId = previousState.blessId;
}
}

private static class RuntimeScalarIterator implements Iterator<RuntimeScalar> {
private final RuntimeScalar scalar;
private boolean hasNext = true;
Expand Down

0 comments on commit b33de15

Please sign in to comment.