init nodes, some basic nodes implementations

This commit is contained in:
ProgramSnail 2025-06-12 23:23:29 +03:00
parent a149766007
commit 07b80ce5cb
46 changed files with 906 additions and 256 deletions

2
.gitignore vendored
View file

@ -36,3 +36,5 @@ build/
### Mac OS ###
.DS_Store
gen/

View file

@ -3,7 +3,9 @@
This is an attempt to implement the LaMa language (https://github.com/PLTools/Lama) with GraalVM and Truffle.
Code is based on:
- https://github.com/graalvm/simplelanguage
- https://github.com/cesquivias/mumbler
- https://github.com/skinny85/graalvm-truffle-tutorial
---

View file

@ -0,0 +1,36 @@
package org.programsnail.truffle_lama;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.programsnail.truffle_lama.nodes.FunctionDispatchNode;
import org.programsnail.truffle_lama.runtime.LamaException;
@ExportLibrary(InteropLibrary.class)
public final class FunctionObject implements TruffleObject {
public final CallTarget callTarget;
public final int argumentCount;
public final FunctionDispatchNode functionDispatchNode;
public FunctionObject(CallTarget callTarget, int argumentCount, FunctionDispatchNode functionDispatchNode) {
this.callTarget = callTarget;
this.argumentCount = argumentCount;
this.functionDispatchNode = functionDispatchNode;
}
@ExportMessage
boolean isExecutable() {
return true;
}
@ExportMessage
Object execute(Object[] arguments) {
if (arguments.length != argumentCount) {
throw new LamaException("Wrong amount of function arguments: " + arguments.length + " instead of " + argumentCount);
}
// TODO: check values of arguments ??
return this.functionDispatchNode.executeDispatch(this, arguments);
}
}

View file

@ -0,0 +1,121 @@
package org.programsnail.truffle_lama;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.programsnail.truffle_lama.runtime.LamaException;
import java.util.*;
@ExportLibrary(InteropLibrary.class)
public final class GlobalScopeObject implements TruffleObject {
private final Map<String, Object> variables = new HashMap<>();
private final Set<String> constants = new HashSet<>();
public boolean newVariable(String name, Object value, boolean isConst) {
Object previousValue = variables.put(name, value);
if (isConst) {
constants.add(name);
}
return previousValue == null;
}
public void newFunction(String name, FunctionObject fun) {
variables.put(name, fun);
}
public boolean updateVariable(String name, Object value) {
if (this.constants.contains(name)) {
throw new LamaException("Can't assign to const var '" + name + "'.");
}
Object updatedValue = this.variables.computeIfPresent(name, (k, v) -> value);
return updatedValue != null;
}
public Object getVariable(String name) {
return this.variables.get(name);
}
// ---
@ExportMessage
boolean isScope() {
return true;
}
@ExportMessage
boolean hasMembers() {
return true;
}
@ExportMessage
Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
return new GlobalVariableNamesObject(this.variables.keySet());
}
@ExportMessage
boolean isMemberReadable(String member) {
return this.variables.containsKey(member);
}
@ExportMessage
Object readMember(String member) throws UnknownIdentifierException {
Object value = this.variables.get(member);
if (null == value) {
throw UnknownIdentifierException.create(member);
}
return value;
}
@ExportMessage
Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
return "global";
}
@ExportMessage
boolean hasLanguage() {
return true;
}
@ExportMessage
Class<? extends TruffleLanguage<?>> getLanguage() {
return LamaLanguage.class;
}
}
@ExportLibrary(InteropLibrary.class)
final class GlobalVariablesNamesObject implements TruffleObject {
private final List<String> names;
GlobalVariablesNamesObject(List<String> names) {
this.names = names;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return this.names.size();
}
@ExportMessage
boolean isArrayElementReadable(long index) {
return index >= 0 && index < this.names.size();
}
@ExportMessage
Object readArrayElement(long index) throws InvalidArrayIndexException {
if (!this.isArrayElementReadable(index)) {
throw InvalidArrayIndexException.create(index);
}
return this.names.get((int) index);
}
}

View file

@ -1,47 +1,59 @@
package org.programsnail.truffle_lama;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import java.awt.*;
// TODO
public class LamaContext {
private final FrameDescriptor globalFrameDescriptor;
private final Namespace globalNamespace;
private final MaterializedFrame globalFrame;
private final LamaLanguage language;
public final class LamaContext {
private static final TruffleLanguage.ContextReference<LamaContext> REF = TruffleLanguage.ContextReference.create(LamaLanguage.class);
public LamaContext() { this(null); }
public LamaContext(LamaLanguage language) {
this.globalFrameDescriptor = new FrameDescriptor();
this.globalNamespace = new Namespace(this.globalFrameDescriptor);
this.globalFrame = this.initGlobalFrame(language);
this.language = language;
public static LamaContext get(Node node) {
return REF.get(node);
}
private MaterializedFrame initGlobalFrame(LamaLanguage language) {
VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(null, this.globalFrameDescriptor);
addGlobalFunctions(language, frame);
return frame.materialize();
}
public final GlobalScopeObject globalScopeObject;
private static void addGlobalFunctions(LamaLanguage language, VirtualFrame virtualFrame) {
// TODO
}
/**
* @return A {@link MaterializedFrame} on the heap that contains all global
* values.
*/
public MaterializedFrame getGlobalFrame() {
return this.globalFrame;
}
public Namespace getGlobalNamespace() {
return this.globalNamespace;
public LamaContext() {
this.globalScopeObject = new GlobalScopeObject();
}
// private final FrameDescriptor globalFrameDescriptor;
// private final Namespace globalNamespace;
// private final MaterializedFrame globalFrame;
// private final LamaLanguage language;
//
// public LamaContext() { this(null); }
//
// public LamaContext(LamaLanguage language) {
// this.globalFrameDescriptor = new FrameDescriptor();
// this.globalNamespace = new Namespace(this.globalFrameDescriptor);
// this.globalFrame = this.initGlobalFrame(language);
// this.language = language;
// }
//
// private MaterializedFrame initGlobalFrame(LamaLanguage language) {
// VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(null, this.globalFrameDescriptor);
// addGlobalFunctions(language, frame);
// return frame.materialize();
// }
//
// private static void addGlobalFunctions(LamaLanguage language, VirtualFrame virtualFrame) {
// // TODO
// }
//
// /**
// * @return A {@link MaterializedFrame} on the heap that contains all global
// * values.
// */
// public MaterializedFrame getGlobalFrame() {
// return this.globalFrame;
// }
//
// public Namespace getGlobalNamespace() {
// return this.globalNamespace;
// }
}

View file

@ -14,6 +14,9 @@ import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
import org.programsnail.truffle_lama.builtins.LamaBuiltinNode;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.nodes.LamaRootNode;
import org.programsnail.truffle_lama.parser.LamaParser;
import java.util.ArrayList;
@ -23,204 +26,185 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@TruffleLanguage.Registration(id = LamaLanguage.ID, name = "Lama", defaultMimeType = LamaLanguage.MIME_TYPE, characterMimeTypes = LamaLanguage.MIME_TYPE, contextPolicy = TruffleLanguage.ContextPolicy.SHARED, fileTypeDetectors = LamaFileDetector.class)
@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, StandardTags.RootBodyTag.class, StandardTags.ExpressionTag.class, DebuggerTags.AlwaysHalt.class,
StandardTags.ReadVariableTag.class, StandardTags.WriteVariableTag.class})
//@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, StandardTags.RootBodyTag.class, StandardTags.ExpressionTag.class, DebuggerTags.AlwaysHalt.class,
// StandardTags.ReadVariableTag.class, StandardTags.WriteVariableTag.class})
public class LamaLanguage extends TruffleLanguage<LamaContext> {
public static volatile int counter; // count class instances
// public static volatile int counter; // count class instances
public static final String ID = "lama";
public static final String MIME_TYPE = "application/x-lama";
private static final Source BUILTIN_SOURCE = Source.newBuilder(ID, "", "LaMa builtin").build();
private static final LanguageReference<LamaLanguage> REFERENCE = LanguageReference.create(LamaLanguage.class);
private static final List<NodeFactory<? extends LamaBuiltinNode>> EXTERNAL_BUILTINS = Collections.synchronizedList(new ArrayList<>());
// private static final Source BUILTIN_SOURCE = Source.newBuilder(ID, "", "LaMa builtin").build();
// private static final LanguageReference<LamaLanguage> REFERENCE = LanguageReference.create(LamaLanguage.class);
// private static final List<NodeFactory<? extends LamaBuiltinNode>> EXTERNAL_BUILTINS = Collections.synchronizedList(new ArrayList<>());
public static final TruffleString.Encoding STRING_ENCODING = TruffleString.Encoding.UTF_16;
// public static final TruffleString.Encoding STRING_ENCODING = TruffleString.Encoding.UTF_16;
private final Assumption singleContext = Truffle.getRuntime().createAssumption("Single Lama context.");
private final Map<NodeFactory<? extends LamaBuiltinNode>, RootCallTarget> builtinTargets = new ConcurrentHashMap<>();
private final Map<TruffleString, RootCallTarget> undefinedFunctions = new ConcurrentHashMap<>();
private final Shape rootShape;
//
public LamaLanguage() {
counter++;
this.rootShape = Shape.newBuilder().layout(LamaObject.class).build();
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
var exprNode = LamaParser.parseLama(this, request.getSource());
var rootNode = new LamaRootNode(exprNode);
return rootNode.getCallTarget();
}
@Override
protected LamaContext createContext(Env env) {
return new LamaContext(this, env, new ArrayList<>(EXTERNAL_BUILTINS));
}
@Override
protected boolean patchContext(LamaContext context, Env newEnv) {
context.patchContext(newEnv);
return true;
}
//
public RootCallTarget getOrCreateUndefinedFunction(TruffleString name) {
RootCallTarget target = undefinedFunctions.get(name);
if (target == null) {
target = new LamaUndefinedFunctionRootNode(this, name).getCallTarget();
RootCallTarget other = undefinedFunctions.putIfAbsent(name, target);
if (other != null) {
target = other;
}
}
return target;
}
public RootCallTarget lookupBuiltin(NodeFactory<? extends LamaBuiltinNode> factory) {
RootCallTarget target = builtinTargets.get(factory);
if (target != null) {
return target;
}
/*
* The builtin node factory is a class that is automatically generated by the Truffle DSL.
* The signature returned by the factory reflects the signature of the @Specialization
*
* methods in the builtin classes.
*/
int argumentCount = factory.getExecutionSignature().size();
LamaExpressionNode[] argumentNodes = new LamaExpressionNode[argumentCount];
/*
* Builtin functions are like normal functions, i.e., the arguments are passed in as an
* Object[] array encapsulated in SLArguments. A SLReadArgumentNode extracts a parameter
* from this array.
*/
for (int i = 0; i < argumentCount; i++) {
argumentNodes[i] = new LamaReadArgumentNode(i);
}
/* Instantiate the builtin node. This node performs the actual functionality. */
LamaBuiltinNode builtinBodyNode = factory.createNode((Object) argumentNodes);
builtinBodyNode.addRootTag();
/* The name of the builtin function is specified via an annotation on the node class. */
TruffleString name = LamaStrings.fromJavaString(lookupNodeInfo(builtinBodyNode.getClass()).shortName());
builtinBodyNode.setUnavailableSourceSection();
/* Wrap the builtin in a RootNode. Truffle requires all AST to start with a RootNode. */
LamamRootNode rootNode = new LammaRootNode(this, new FrameDescriptor(), builtinBodyNode, BUILTIN_SOURCE.createUnavailableSection(), name);
/*
* Register the builtin function in the builtin registry. Call targets for builtins may be
* reused across multiple contexts.
*/
RootCallTarget newTarget = rootNode.getCallTarget();
RootCallTarget oldTarget = builtinTargets.putIfAbsent(factory, newTarget);
if (oldTarget != null) {
return oldTarget;
}
return newTarget;
}
public static NodeInfo lookupNodeInfo(Class<?> c) {
if (c == null) {
return null;
}
NodeInfo info = c.getAnnotation(NodeInfo.class);
if (info != null) {
return info;
} else {
return lookupNodeInfo(c.getSuperclass());
}
}
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
Source source = request.getSource();
Map<TruffleString, RootCallTarget> functions;
/*
* Parse the provided source. At this point, we do not have a SLContext yet. Registration of
* the functions with the SLContext happens lazily in SLEvalRootNode.
*/
if (request.getArgumentNames().isEmpty()) {
functions = LamaParser.parseLama(this, source);
} else {
StringBuilder sb = new StringBuilder();
sb.append("function main(");
String sep = "";
for (String argumentName : request.getArgumentNames()) {
sb.append(sep);
sb.append(argumentName);
sep = ",";
}
sb.append(") { return ");
sb.append(source.getCharacters());
sb.append(";}");
String language = source.getLanguage() == null ? ID : source.getLanguage();
Source decoratedSource = Source.newBuilder(language, sb.toString(), source.getName()).build();
functions = LamaParser.parseLama(this, decoratedSource);
}
// TODO: execute source itself, not main function
return ;
}
@Override
protected void initializeMultipleContexts() {
singleContext.invalidate();
}
public boolean isSingleContext() {
return singleContext.isValid();
}
@Override
protected Object getLanguageView(LamaContext context, Object value) {
return LamaLanguageView.create(value);
}
@Override
protected boolean isVisible(LamaContext context, Object value) {
return !InteropLibrary.getFactory().getUncached(value).isNull(value);
return new LamaContext();
}
@Override
protected Object getScope(LamaContext context) {
return context.getFunctionRegistry().getFunctionsObject();
}
public Shape getRootShape() {
return rootShape;
}
/**
* Allocate an empty object. All new objects initially have no properties. Properties are added
* when they are first stored, i.e., the store triggers a shape change of the object.
*/
public LamaObject createObject(AllocationReporter reporter) {
reporter.onEnter(null, 0, AllocationReporter.SIZE_UNKNOWN);
LamaObject object = new LamaObject(rootShape);
reporter.onReturnValue(object, 0, AllocationReporter.SIZE_UNKNOWN);
return object;
return context.globalScopeObject;
}
//
public static LamaLanguage get(Node node) {
return REFERENCE.get(node);
}
// //
//
public static void installBuiltin(NodeFactory<? extends LamaBuiltinNode> builtin) {
EXTERNAL_BUILTINS.add(builtin);
}
@Override
protected void exitContext(LamaContext context, ExitMode exitMode, int exitCode) {
/*
* Runs shutdown hooks during explicit exit triggered by TruffleContext#closeExit(Node, int)
* or natural exit triggered during natural context close.
*/
context.runShutdownHooks();
}
// public LamaLanguage() {
//// counter++;
// }
//
// @Override
// protected LamaContext createContext(Env env) {
// return new LamaContext(this, env, new ArrayList<>(EXTERNAL_BUILTINS));
// }
//
// @Override
// protected boolean patchContext(LamaContext context, Env newEnv) {
// context.patchContext(newEnv);
// return true;
// }
//
// //
//
// public RootCallTarget getOrCreateUndefinedFunction(TruffleString name) {
// RootCallTarget target = undefinedFunctions.get(name);
// if (target == null) {
// target = new LamaUndefinedFunctionRootNode(this, name).getCallTarget();
// RootCallTarget other = undefinedFunctions.putIfAbsent(name, target);
// if (other != null) {
// target = other;
// }
// }
// return target;
// }
//
// public RootCallTarget lookupBuiltin(NodeFactory<? extends LamaBuiltinNode> factory) {
// RootCallTarget target = builtinTargets.get(factory);
// if (target != null) {
// return target;
// }
//
// /*
// * The builtin node factory is a class that is automatically generated by the Truffle DSL.
// * The signature returned by the factory reflects the signature of the @Specialization
// *
// * methods in the builtin classes.
// */
// int argumentCount = factory.getExecutionSignature().size();
// LamaExpressionNode[] argumentNodes = new LamaExpressionNode[argumentCount];
// /*
// * Builtin functions are like normal functions, i.e., the arguments are passed in as an
// * Object[] array encapsulated in SLArguments. A SLReadArgumentNode extracts a parameter
// * from this array.
// */
// for (int i = 0; i < argumentCount; i++) {
// argumentNodes[i] = new LamaReadArgumentNode(i);
// }
// /* Instantiate the builtin node. This node performs the actual functionality. */
// LamaBuiltinNode builtinBodyNode = factory.createNode((Object) argumentNodes);
// builtinBodyNode.addRootTag();
// /* The name of the builtin function is specified via an annotation on the node class. */
// TruffleString name = LamaStrings.fromJavaString(lookupNodeInfo(builtinBodyNode.getClass()).shortName());
// builtinBodyNode.setUnavailableSourceSection();
//
// /* Wrap the builtin in a RootNode. Truffle requires all AST to start with a RootNode. */
// LamaRootNode rootNode = new LamaRootNode(this, new FrameDescriptor(), builtinBodyNode, BUILTIN_SOURCE.createUnavailableSection(), name);
//
// /*
// * Register the builtin function in the builtin registry. Call targets for builtins may be
// * reused across multiple contexts.
// */
// RootCallTarget newTarget = rootNode.getCallTarget();
// RootCallTarget oldTarget = builtinTargets.putIfAbsent(factory, newTarget);
// if (oldTarget != null) {
// return oldTarget;
// }
// return newTarget;
// }
//
// public static NodeInfo lookupNodeInfo(Class<?> c) {
// if (c == null) {
// return null;
// }
// NodeInfo info = c.getAnnotation(NodeInfo.class);
// if (info != null) {
// return info;
// } else {
// return lookupNodeInfo(c.getSuperclass());
// }
// }
//
//
// @Override
// protected void initializeMultipleContexts() {
// singleContext.invalidate();
// }
//
// public boolean isSingleContext() {
// return singleContext.isValid();
// }
//
// @Override
// protected Object getLanguageView(LamaContext context, Object value) {
// return LamaLanguageView.create(value);
// }
//
// @Override
// protected boolean isVisible(LamaContext context, Object value) {
// return !InteropLibrary.getFactory().getUncached(value).isNull(value);
// }
//
// @Override
// protected Object getScope(LamaContext context) {
// return context.getFunctionRegistry().getFunctionsObject();
// }
//
// public Shape getRootShape() {
// return rootShape;
// }
//
// /**
// * Allocate an empty object. All new objects initially have no properties. Properties are added
// * when they are first stored, i.e., the store triggers a shape change of the object.
// */
// public LamaObject createObject(AllocationReporter reporter) {
// reporter.onEnter(null, 0, AllocationReporter.SIZE_UNKNOWN);
// LamaObject object = new LamaObject(rootShape);
// reporter.onReturnValue(object, 0, AllocationReporter.SIZE_UNKNOWN);
// return object;
// }
//
// //
//
// public static LamaLanguage get(Node node) {
// return REFERENCE.get(node);
// }
//
// //
//
// public static void installBuiltin(NodeFactory<? extends LamaBuiltinNode> builtin) {
// EXTERNAL_BUILTINS.add(builtin);
// }
//
// @Override
// protected void exitContext(LamaContext context, ExitMode exitMode, int exitCode) {
// /*
// * Runs shutdown hooks during explicit exit triggered by TruffleContext#closeExit(Node, int)
// * or natural exit triggered during natural context close.
// */
// context.runShutdownHooks();
// }
}

View file

@ -1,7 +0,0 @@
package org.programsnail.truffle_lama;
import com.oracle.truffle.api.dsl.ImplicitCast;
import com.oracle.truffle.api.dsl.TypeSystem;
@TypeSystem({long.class, boolean.class, String.class})
public class LamaTypes {}

View file

@ -6,6 +6,9 @@ import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
@NodeChild(value = "arguments", type = LamaExpressionNode[].class)
@GenerateNodeFactory
public abstract class LamaBuiltinNode extends LamaExpressionNode {

View file

@ -0,0 +1,34 @@
package org.programsnail.truffle_lama.nodes;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.Node;
import org.programsnail.truffle_lama.FunctionObject;
import org.programsnail.truffle_lama.runtime.LamaException;
public abstract class FunctionDispatchNode extends Node {
public abstract Object executeDispatch(Object function, Object[] arguments);
@Specialization(guards = "function.callTarget == directCallNode.getCallTarget()", limit = "2")
protected static Object dispatchDirectly(FunctionObject function,
Object[] arguments,
@Cached("create(function.callTarget)")
DirectCallNode directCallNode) {
return directCallNode.call(arguments); // TODO: check arguments count ??
}
@Specialization(replaces = "dispatchDirectly")
protected static Object dispatchIndirectly(FunctionObject function,
Object[] arguments,
IndirectCallNode indirectCallNode) {
return indirectCallNode.call(function.callTarget, arguments); // TODO: check arguments count ??
}
@Fallback
protected static Object targetIsNotAFunction(Object object, Object[] arguments) {
throw new LamaException("Called '" + object + "' is not a function");
}
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes;
public class LamaDeclNode {
}

View file

@ -3,13 +3,13 @@ package org.programsnail.truffle_lama.nodes;
import com.oracle.truffle.api.dsl.TypeSystemReference;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.*;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
@TypeSystemReference(SLTypes.class)
@TypeSystemReference(LamaTypes.class)
@NodeInfo(description = "The abstract base node for all expressions")
@GenerateWrapper
public abstract class LamaExpressionNode extends LamaStatementNode {
public abstract class LamaExpressionNode extends Node {
private boolean hasExpressionTag;
@ -17,29 +17,21 @@ public abstract class LamaExpressionNode extends LamaStatementNode {
* The execute method when no specialization is possible. This is the most general case,
* therefore it must be provided by all subclasses.
*/
public abstract Object executeGeneric(VirtualFrame frame);
public abstract Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException;
/**
* When we use an expression at places where a {@link LamaStatementNode statement} is already
* sufficient, the return value is just discarded.
* The return value is just discarded.
*/
@Override
public void executeVoid(VirtualFrame frame) {
executeGeneric(frame);
}
// LamaExpressionNodeWrapper is generated (?)
@Override
public InstrumentableNode.WrapperNode createWrapper(ProbeNode probe) {
return new LamaExpressionNodeWrapper(this, probe);
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if (tag == StandardTags.ExpressionTag.class) {
return hasExpressionTag;
}
return super.hasTag(tag);
return false; // no super tags
}
/**
@ -57,6 +49,7 @@ public abstract class LamaExpressionNode extends LamaStatementNode {
// LamaTypesGen is generated from LamaTypes
// TODO
public long executeLong(VirtualFrame frame) throws UnexpectedResultException {
return LamaTypesGen.expectLong(executeGeneric(frame));
}

View file

@ -0,0 +1,21 @@
package org.programsnail.truffle_lama.nodes;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;
public class LamaRootNode extends RootNode {
@SuppressWarnings("FieldMayBeFinal")
@Child
private LamaExpressionNode exprNode;
public LamaRootNode(LamaExpressionNode exprNode) {
super(null);
this.exprNode = exprNode;
}
@Override
public Object execute(VirtualFrame frame) {
return this.exprNode.executeGeneric(frame);
}
}

View file

@ -0,0 +1,6 @@
package org.programsnail.truffle_lama.nodes;
import com.oracle.truffle.api.dsl.TypeSystem;
@TypeSystem({long.class, boolean.class, String.class})
public abstract class LamaTypes {}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.controlflow;
public class LamaCaseNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.controlflow;
public class LamaControlNode {
}

View file

@ -0,0 +1,52 @@
package org.programsnail.truffle_lama.nodes.controlflow;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RepeatingNode;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.runtime.LamaUnit;
public final class LamaDoWhileNode extends LamaExpressionNode {
@SuppressWarnings("FieldMayBeFinal")
@Child
private LoopNode loopNode;
public LamaDoWhileNode(LamaExpressionNode conditionExpr, LamaExpressionNode bodyExpr) {
this.loopNode = Truffle.getRuntime().createLoopNode(
new WhileRepeatingNode(conditionExpr, bodyExpr));
}
@Override
public Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException {
this.loopNode.execute(frame);
return LamaUnit.INSTANCE;
}
private static final class WhileRepeatingNode extends Node implements RepeatingNode {
@SuppressWarnings("FieldMayBeFinal")
@Child
private LamaExpressionNode conditionExpr, bodyExpr;
public WhileRepeatingNode(LamaExpressionNode conditionExpr, LamaExpressionNode bodyExpr) {
this.conditionExpr = conditionExpr;
this.bodyExpr = bodyExpr;
}
@Override
public boolean executeRepeating(VirtualFrame frame) {
try {
this.bodyExpr.executeGeneric(frame);
// NOTE: try/catch for break/continue
if (!this.conditionExpr.executeBoolean(frame)) {
return false;
}
return false;
} catch (UnexpectedResultException e) { // TODO: handle in proper way
return false;
}
}
}
}

View file

@ -0,0 +1,27 @@
package org.programsnail.truffle_lama.nodes.controlflow;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
public final class LamaIfNode extends LamaExpressionNode {
@Child
private LamaExpressionNode conditionExpr;
@Child
private LamaExpressionNode thenExpr;
@Child
private LamaExpressionNode elseExpr;
private final ConditionProfile condition = ConditionProfile.create();
@Override
public Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException {
if (this.condition.profile(this.conditionExpr.executeBoolean(frame))) {
return this.thenExpr.executeGeneric(frame);
}
return this.elseExpr; // TODO: cases with no else
}
}

View file

@ -0,0 +1,52 @@
package org.programsnail.truffle_lama.nodes.controlflow;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RepeatingNode;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.runtime.LamaUnit;
public final class LamaWhileNode extends LamaExpressionNode {
@SuppressWarnings("FieldMayBeFinal")
@Child
private LoopNode loopNode;
public LamaWhileNode(LamaExpressionNode conditionExpr, LamaExpressionNode bodyExpr) {
this.loopNode = Truffle.getRuntime().createLoopNode(
new WhileRepeatingNode(conditionExpr, bodyExpr));
}
@Override
public Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException {
this.loopNode.execute(frame);
return LamaUnit.INSTANCE;
}
private static final class WhileRepeatingNode extends Node implements RepeatingNode {
@SuppressWarnings("FieldMayBeFinal")
@Child
private LamaExpressionNode conditionExpr, bodyExpr;
public WhileRepeatingNode(LamaExpressionNode conditionExpr, LamaExpressionNode bodyExpr) {
this.conditionExpr = conditionExpr;
this.bodyExpr = bodyExpr;
}
@Override
public boolean executeRepeating(VirtualFrame frame) {
try {
if (!this.conditionExpr.executeBoolean(frame)) {
return false;
}
this.bodyExpr.executeGeneric(frame);
// NOTE: try/catch for break/continue
return false;
} catch (UnexpectedResultException e) { // TODO: handle in proper way
return false;
}
}
}
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaArrayNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaAssignNode {
}

View file

@ -0,0 +1,52 @@
package org.programsnail.truffle_lama.nodes.expression;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeField;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
@NodeChild("leftNode")
@NodeChild("rightNode")
@NodeField(name = "op", type = String.class)
public abstract class LamaBinopNode extends LamaExpressionNode {
@Child
LamaExpressionNode leftNode, rightNode;
@Specialization(rewriteOn = IllegalStateException.class)
protected long numOp(long leftValue, long rightValue) {
return switch (op) {
case "+" -> leftValue + rightValue;
case "-" -> leftValue - rightValue;
case "/" -> leftValue / rightValue;
case "%" -> leftValue % rightValue;
default -> throw new IllegalStateException();
};
}
@Specialization(replaces = "numOp")
protected boolean compOp(long leftValue, long rightValue) {
return switch (op) {
case "<" -> leftValue < rightValue;
case "<=" -> leftValue <= rightValue;
case ">" -> leftValue > rightValue;
case ">=" -> leftValue >= rightValue;
case "==" -> leftValue == rightValue;
case "!=" -> leftValue != rightValue;
default -> throw new IllegalStateException();
};
}
@Specialization
protected boolean boolOp(boolean leftValue, boolean rightValue) {
return switch (op) {
case "&&" -> leftValue && rightValue;
case "!!" -> leftValue || rightValue;
default -> throw new IllegalStateException();
};
}
String op;
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaCallNode {
}

View file

@ -0,0 +1,28 @@
package org.programsnail.truffle_lama.nodes.expression;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
public final class LamaConstNode extends LamaExpressionNode {
private final int value;
public LamaConstNode(int value) {
this.value = value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return this.executeLong(frame);
}
@Override
public long executeLong(VirtualFrame frame) {
return value;
}
@Override
public boolean executeBoolean(VirtualFrame frame) {
return value != 0;
}
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaElemNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaElemRefNode {
}

View file

@ -0,0 +1,16 @@
package org.programsnail.truffle_lama.nodes.expression;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.runtime.LamaUnit;
public final class LamaIgnoreNode extends LamaExpressionNode {
@Child
LamaExpressionNode node;
@Override
public Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException {
return LamaUnit.INSTANCE;
}
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaIntrinsicNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaLambdaNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaLeaveNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaRefNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaScopeNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaSeqNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaSexpNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaSkipNode {
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaStringNode {
}

View file

@ -0,0 +1,13 @@
package org.programsnail.truffle_lama.nodes.expression;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.runtime.LamaUnit;
public final class LamaUnitNode extends LamaExpressionNode {
@Override
public Object executeGeneric(VirtualFrame frame) throws UnexpectedResultException {
return LamaUnit.INSTANCE;
}
}

View file

@ -0,0 +1,4 @@
package org.programsnail.truffle_lama.nodes.expression;
public class LamaVarNode {
}

View file

@ -40,7 +40,7 @@
*/
/*
* The parser and lexer need to be generated using "mx create-sl-parser".
* The parser and lexer need to be generated using "mx create-lama-parser". ???
*/
grammar Lama;
@ -58,7 +58,6 @@ import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
import org.programsnail.truffle_lama.LamaLanguage;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.nodes.LamaStatementNode;
}
@lexer::header
@ -91,11 +90,11 @@ private static void throwParseError(Source source, int line, int charPositionInL
int col = charPositionInLine + 1;
String location = "-- line " + line + " col " + col + ": ";
int length = token == null ? 1 : Math.max(token.getStopIndex() - token.getStartIndex(), 0);
throw new SLParseError(source, line, col, length, String.format("Error(s) parsing script:%n" + location + message));
throw new LamaParseError(source, line, col, length, String.format("Error(s) parsing script:%n" + location + message));
}
public static Map<TruffleString, RootCallTarget> parseLama(LamaLanguage language, Source source) {
LamaLexer lexer = new LamamLexer(CharStreams.fromString(source.getCharacters().toString()));
public static LamaExpressionNode parseLama(LamaLanguage language, Source source) {
LamaLexer lexer = new LamaLexer(CharStreams.fromString(source.getCharacters().toString()));
LamaParser parser = new LamaParser(new CommonTokenStream(lexer));
lexer.removeErrorListeners();
parser.removeErrorListeners();
@ -105,7 +104,7 @@ public static Map<TruffleString, RootCallTarget> parseLama(LamaLanguage language
parser.factory = new LamaNodeFactory(language, source);
parser.source = source;
parser.lama();
return parser.factory.getAllFunctions();
return parser.factory.getRootExpr();
}
}
@ -142,15 +141,15 @@ level : ('at' | 'before' | 'after') INFIX;
//
expression returns [SLExpressionNode result]: basic_expression (';' expression)?;
basic_expression returns [SLExpressionNode result]: binary_expression;
binary_expression returns [SLExpressionNode result]: postfix_expression (INFIX postfix_expression)*;
postfix_expression returns [SLExpressionNode result]: ('-')? primary (postfix)*;
expression returns [LamaExpressionNode result]: basic_expression (';' expression)?;
basic_expression returns [LamaExpressionNode result]: binary_expression;
binary_expression returns [LamaExpressionNode result]: postfix_expression (INFIX postfix_expression)*;
postfix_expression returns [LamaExpressionNode result]: ('-')? primary (postfix)*;
postfix :
'(' expression (',' expression)* ')'
| '[' expression ']';
primary returns [SLExpressionNode result]:
primary returns [LamaExpressionNode result]:
DECIMAL_LITERAL
| STRING_LITERAL
| CHAR_LITERAL

View file

@ -5,9 +5,12 @@ package org.programsnail.truffle_lama.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
public class LamaLexer extends Lexer {

View file

@ -0,0 +1,19 @@
package org.programsnail.truffle_lama.parser;
import com.oracle.truffle.api.source.Source;
import org.programsnail.truffle_lama.LamaLanguage;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
public class LamaNodeFactory {
public LamaNodeFactory(LamaLanguage language, Source source) {
// TODO
}
public LamaExpressionNode getRootExpr() {
// TODO
return null;
}
// TODO: build expr tree from parsed tokens
}

View file

@ -0,0 +1,54 @@
package org.programsnail.truffle_lama.parser;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.ExceptionType;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
@ExportLibrary(InteropLibrary.class)
public class LamaParseError extends AbstractTruffleException {
public static final long serialVersionUID = 1L;
private final Source source;
private final int line;
private final int column;
private final int length;
public LamaParseError(Source source, int line, int column, int length, String message) {
super(message);
this.source = source;
this.line = line;
this.column = column;
this.length = length;
}
/**
* Note that any subclass of {@link AbstractTruffleException} must always return
* <code>true</code> for {@link InteropLibrary#isException(Object)}. That is why it is correct
* to export {@link #getExceptionType()} without implementing
* {@link InteropLibrary#isException(Object)}.
*/
@ExportMessage
ExceptionType getExceptionType() {
return ExceptionType.PARSE_ERROR;
}
@ExportMessage
boolean hasSourceLocation() {
return source != null;
}
@ExportMessage(name = "getSourceLocation")
@CompilerDirectives.TruffleBoundary
SourceSection getSourceSection() throws UnsupportedMessageException {
if (source == null) {
throw UnsupportedMessageException.create();
}
return source.createSection(line, column, length);
}
}

View file

@ -3,6 +3,7 @@ package org.programsnail.truffle_lama.parser;
// DO NOT MODIFY - generated from Lama.g4
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -11,12 +12,15 @@ import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
import org.programsnail.truffle_lama.LamaLanguage;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.nodes.LamaStatementNode;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.*;
import org.antlr.v4.runtime.tree.*;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
public class LamaParser extends Parser {
@ -153,11 +157,11 @@ public class LamaParser extends Parser {
int col = charPositionInLine + 1;
String location = "-- line " + line + " col " + col + ": ";
int length = token == null ? 1 : Math.max(token.getStopIndex() - token.getStartIndex(), 0);
throw new SLParseError(source, line, col, length, String.format("Error(s) parsing script:%n" + location + message));
throw new LamaParseError(source, line, col, length, String.format("Error(s) parsing script:%n" + location + message));
}
public static Map<TruffleString, RootCallTarget> parseLama(LamaLanguage language, Source source) {
LamaLexer lexer = new LamamLexer(CharStreams.fromString(source.getCharacters().toString()));
public static LamaExpressionNode parseLama(LamaLanguage language, Source source) {
LamaLexer lexer = new LamaLexer(CharStreams.fromString(source.getCharacters().toString()));
LamaParser parser = new LamaParser(new CommonTokenStream(lexer));
lexer.removeErrorListeners();
parser.removeErrorListeners();
@ -167,7 +171,7 @@ public class LamaParser extends Parser {
parser.factory = new LamaNodeFactory(language, source);
parser.source = source;
parser.lama();
return parser.factory.getAllFunctions();
return parser.factory.getRootExpr();
}
public LamaParser(TokenStream input) {
@ -351,7 +355,7 @@ public class LamaParser extends Parser {
setState(92);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,1,_ctx);
} while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
} while ( _alt!=2 && _alt!= ATN.INVALID_ALT_NUMBER );
setState(95);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) {
@ -1057,7 +1061,7 @@ public class LamaParser extends Parser {
@SuppressWarnings("CheckReturnValue")
public static class ExpressionContext extends ParserRuleContext {
public SLExpressionNode result;
public LamaExpressionNode result;
public Basic_expressionContext basic_expression() {
return getRuleContext(Basic_expressionContext.class,0);
}
@ -1119,7 +1123,7 @@ public class LamaParser extends Parser {
@SuppressWarnings("CheckReturnValue")
public static class Basic_expressionContext extends ParserRuleContext {
public SLExpressionNode result;
public LamaExpressionNode result;
public Binary_expressionContext binary_expression() {
return getRuleContext(Binary_expressionContext.class,0);
}
@ -1165,7 +1169,7 @@ public class LamaParser extends Parser {
@SuppressWarnings("CheckReturnValue")
public static class Binary_expressionContext extends ParserRuleContext {
public SLExpressionNode result;
public LamaExpressionNode result;
public List<Postfix_expressionContext> postfix_expression() {
return getRuleContexts(Postfix_expressionContext.class);
}
@ -1235,7 +1239,7 @@ public class LamaParser extends Parser {
@SuppressWarnings("CheckReturnValue")
public static class Postfix_expressionContext extends ParserRuleContext {
public SLExpressionNode result;
public LamaExpressionNode result;
public PrimaryContext primary() {
return getRuleContext(PrimaryContext.class,0);
}
@ -1400,7 +1404,7 @@ public class LamaParser extends Parser {
@SuppressWarnings("CheckReturnValue")
public static class PrimaryContext extends ParserRuleContext {
public SLExpressionNode result;
public LamaExpressionNode result;
public TerminalNode DECIMAL_LITERAL() { return getToken(LamaParser.DECIMAL_LITERAL, 0); }
public TerminalNode STRING_LITERAL() { return getToken(LamaParser.STRING_LITERAL, 0); }
public TerminalNode CHAR_LITERAL() { return getToken(LamaParser.CHAR_LITERAL, 0); }

View file

@ -0,0 +1,14 @@
package org.programsnail.truffle_lama.runtime;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.nodes.Node;
public class LamaException extends AbstractTruffleException {
public LamaException(String message) {
this(message, null);
}
public LamaException(String message, Node location) {
super(message, location);
}
}

View file

@ -0,0 +1,13 @@
package org.programsnail.truffle_lama.runtime;
import java.util.List;
public final class LamaSExp {
public String name;
public List<Object> elements;
LamaSExp(String name, List<Object> elements) {
this.name = name;
this.elements = elements;
}
}

View file

@ -0,0 +1,8 @@
package org.programsnail.truffle_lama.runtime;
// TODO: truffle object
public final class LamaUnit {
public static final LamaUnit INSTANCE = new LamaUnit();
private LamaUnit() { }
}

View file

@ -0,0 +1,17 @@
import com.oracle.truffle.api.CallTarget;
import org.junit.Test;
import org.programsnail.truffle_lama.nodes.LamaExpressionNode;
import org.programsnail.truffle_lama.nodes.LamaRootNode;
public class BasicNodesTest {
@Test
public void basic_test() {
LamaExpressionNode exprNode = null; // TODO
var rootNode = new LamaRootNode(exprNode);
CallTarget callTarget = rootNode.getCallTarget();
var result = callTarget.call();
// TODO: check
}
}