The Truffle Framework is an open-source library for building programming language implementations as interpreters for self-modifying Abstract Syntax Trees (ASTs). When combined with the GraalVM compiler, Truffle can provide high-performance execution of guest languages on the Java Virtual Machine.
Truffle Architecture Overview
Guest Language → Truffle Parser → AST Interpreter → Graal JIT → Native Code ↓ Language Implementation ↓ Polyglot Programming
Core Dependencies
<!-- pom.xml -->
<properties>
<graalvm.version>23.0.0</graalvm.version>
<truffle.version>23.0.0</truffle.version>
</properties>
<dependencies>
<!-- Truffle API -->
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-api</artifactId>
<version>${truffle.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-dsl-processor</artifactId>
<version>${truffle.version}</version>
<scope>provided</scope>
</dependency>
<!-- GraalVM SDK -->
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>${graalvm.version}</version>
</dependency>
<!-- JSON for configuration -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
</dependencies>
Basic Language Implementation Structure
Language Registration
package com.example.tinylang;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
@TruffleLanguage.Registration(
id = "tinylang",
name = "Tiny Language",
defaultMimeType = TinyLanguage.MIME_TYPE,
characterMimeTypes = TinyLanguage.MIME_TYPE,
contextPolicy = TruffleLanguage.ContextPolicy.SHARED
)
public class TinyLanguage extends TruffleLanguage<TinyLanguageContext> {
public static final String MIME_TYPE = "application/x-tinylang";
@Override
protected TinyLanguageContext createContext(Env env) {
return new TinyLanguageContext(env);
}
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
Source source = request.getSource();
Node rootNode = new TinyLanguageParser().parse(this, source);
return Truffle.getRuntime().createCallTarget(rootNode);
}
@Override
protected boolean isObjectOfLanguage(Object object) {
return false;
}
}
// Language Context
class TinyLanguageContext {
private final Env env;
private final GlobalScope globalScope;
public TinyLanguageContext(Env env) {
this.env = env;
this.globalScope = new GlobalScope();
}
public Env getEnv() { return env; }
public GlobalScope getGlobalScope() { return globalScope; }
}
// Global Scope for variables
class GlobalScope {
private final Map<String, Object> variables = new HashMap<>();
public void defineVariable(String name, Object value) {
variables.put(name, value);
}
public Object getVariable(String name) {
return variables.get(name);
}
public boolean hasVariable(String name) {
return variables.containsKey(name);
}
}
AST Node Foundation
Base Node Classes
package com.example.tinylang.nodes;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
// Base class for all expression nodes
public abstract class ExpressionNode extends Node {
public abstract Object executeGeneric(VirtualFrame frame);
public long executeLong(VirtualFrame frame) throws UnexpectedResultException {
Object value = executeGeneric(frame);
if (value instanceof Long) {
return (Long) value;
}
throw new UnexpectedResultException(value);
}
public double executeDouble(VirtualFrame frame) throws UnexpectedResultException {
Object value = executeGeneric(frame);
if (value instanceof Double) {
return (Double) value;
}
throw new UnexpectedResultException(value);
}
public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {
Object value = executeGeneric(frame);
if (value instanceof Boolean) {
return (Boolean) value;
}
throw new UnexpectedResultException(value);
}
}
// Base class for statement nodes
public abstract class StatementNode extends Node {
public abstract void executeVoid(VirtualFrame frame);
}
// Root node of the AST
@NodeInfo(language = "Tiny Language", description = "The root of Tiny Language AST")
public class TinyLanguageRootNode extends ExpressionNode {
@Child private ExpressionNode body;
private final TinyLanguage language;
public TinyLanguageRootNode(TinyLanguage language, ExpressionNode body) {
this.language = language;
this.body = body;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return body.executeGeneric(frame);
}
}
Expression Nodes
Literal Nodes
package com.example.tinylang.nodes.expressions;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
// Integer literal node
@NodeInfo(description = "Integer literal")
public abstract class IntLiteralNode extends ExpressionNode {
private final long value;
public IntLiteralNode(long value) {
this.value = value;
}
@Specialization
public long executeLong() {
return value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return executeLong(frame);
}
}
// Double literal node
@NodeInfo(description = "Double literal")
public abstract class DoubleLiteralNode extends ExpressionNode {
private final double value;
public DoubleLiteralNode(double value) {
this.value = value;
}
@Specialization
public double executeDouble() {
return value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return executeDouble(frame);
}
}
// String literal node
@NodeInfo(description = "String literal")
public abstract class StringLiteralNode extends ExpressionNode {
private final String value;
public StringLiteralNode(String value) {
this.value = value;
}
@Specialization
public String executeString() {
return value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return executeString();
}
}
// Boolean literal node
@NodeInfo(description = "Boolean literal")
public abstract class BooleanLiteralNode extends ExpressionNode {
private final boolean value;
public BooleanLiteralNode(boolean value) {
this.value = value;
}
@Specialization
public boolean executeBoolean() {
return value;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
return executeBoolean();
}
}
Arithmetic Operation Nodes
package com.example.tinylang.nodes.expressions;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
// Addition node
@NodeInfo(description = "Addition operation")
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class AddNode extends ExpressionNode {
@Specialization(rewriteOn = ArithmeticException.class)
protected long addLong(long left, long right) {
return Math.addExact(left, right);
}
@Specialization
protected double addDouble(double left, double right) {
return left + right;
}
@Specialization
protected String addString(String left, String right) {
return left + right;
}
@Fallback
protected Object addGeneric(Object left, Object right) {
return left.toString() + right.toString();
}
}
// Subtraction node
@NodeInfo(description = "Subtraction operation")
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class SubtractNode extends ExpressionNode {
@Specialization(rewriteOn = ArithmeticException.class)
protected long subtractLong(long left, long right) {
return Math.subtractExact(left, right);
}
@Specialization
protected double subtractDouble(double left, double right) {
return left - right;
}
@Fallback
protected Object subtractGeneric(Object left, Object right) {
throw new UnsupportedOperationException("Cannot subtract non-numeric values");
}
}
// Multiplication node
@NodeInfo(description = "Multiplication operation")
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class MultiplyNode extends ExpressionNode {
@Specialization(rewriteOn = ArithmeticException.class)
protected long multiplyLong(long left, long right) {
return Math.multiplyExact(left, right);
}
@Specialization
protected double multiplyDouble(double left, double right) {
return left * right;
}
@Fallback
protected Object multiplyGeneric(Object left, Object right) {
throw new UnsupportedOperationException("Cannot multiply non-numeric values");
}
}
// Division node
@NodeInfo(description = "Division operation")
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class DivideNode extends ExpressionNode {
@Specialization
protected long divideLong(long left, long right) {
return left / right;
}
@Specialization
protected double divideDouble(double left, double right) {
return left / right;
}
@Fallback
protected Object divideGeneric(Object left, Object right) {
throw new UnsupportedOperationException("Cannot divide non-numeric values");
}
}
Comparison and Logical Nodes
package com.example.tinylang.nodes.expressions;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
// Equal comparison
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class EqualNode extends ExpressionNode {
@Specialization
protected boolean equalLong(long left, long right) {
return left == right;
}
@Specialization
protected boolean equalDouble(double left, double right) {
return left == right;
}
@Specialization
protected boolean equalBoolean(boolean left, boolean right) {
return left == right;
}
@Specialization
protected boolean equalString(String left, String right) {
return left.equals(right);
}
@Specialization
protected boolean equalObject(Object left, Object right) {
return left.equals(right);
}
}
// Less than comparison
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class LessThanNode extends ExpressionNode {
@Specialization
protected boolean lessThanLong(long left, long right) {
return left < right;
}
@Specialization
protected boolean lessThanDouble(double left, double right) {
return left < right;
}
@Specialization
protected boolean lessThanString(String left, String right) {
return left.compareTo(right) < 0;
}
}
// Logical AND node
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class LogicalAndNode extends ExpressionNode {
@Specialization
protected boolean logicalAnd(boolean left, boolean right) {
return left && right;
}
}
// Logical OR node
@NodeChildren({@NodeChild("left"), @NodeChild("right")})
public abstract class LogicalOrNode extends ExpressionNode {
@Specialization
protected boolean logicalOr(boolean left, boolean right) {
return left || right;
}
}
// Logical NOT node
@NodeChild("operand")
public abstract class LogicalNotNode extends ExpressionNode {
@Specialization
protected boolean logicalNot(boolean operand) {
return !operand;
}
}
Variable and Scope Nodes
package com.example.tinylang.nodes;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeField;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame;
// Read variable node
@NodeInfo(description = "Read variable")
@NodeField(name = "slot", type = FrameSlot.class)
public abstract class ReadVariableNode extends ExpressionNode {
protected abstract FrameSlot getSlot();
@Specialization(guards = "isLong(frame)")
protected long readLong(VirtualFrame frame) {
return frame.getLong(getSlot());
}
@Specialization(guards = "isDouble(frame)")
protected double readDouble(VirtualFrame frame) {
return frame.getDouble(getSlot());
}
@Specialization(guards = "isBoolean(frame)")
protected boolean readBoolean(VirtualFrame frame) {
return frame.getBoolean(getSlot());
}
@Specialization(replaces = {"readLong", "readDouble", "readBoolean"})
protected Object readObject(VirtualFrame frame) {
if (!frame.isObject(getSlot())) {
CompilerDirectives.transferToInterpreter();
Object result = frame.getValue(getSlot());
frame.setObject(getSlot(), result);
return result;
}
return frame.getObject(getSlot());
}
protected boolean isLong(VirtualFrame frame) {
return frame.getFrameDescriptor().getFrameSlotKind(getSlot()) == FrameSlotKind.Long;
}
protected boolean isDouble(VirtualFrame frame) {
return frame.getFrameDescriptor().getFrameSlotKind(getSlot()) == FrameSlotKind.Double;
}
protected boolean isBoolean(VirtualFrame frame) {
return frame.getFrameDescriptor().getFrameSlotKind(getSlot()) == FrameSlotKind.Boolean;
}
}
// Write variable node
@NodeInfo(description = "Write variable")
@NodeField(name = "slot", type = FrameSlot.class)
public abstract class WriteVariableNode extends StatementNode {
@Child private ExpressionNode valueNode;
protected abstract FrameSlot getSlot();
public WriteVariableNode(ExpressionNode valueNode) {
this.valueNode = valueNode;
}
@Specialization(guards = "isLong(value)")
protected void writeLong(VirtualFrame frame, long value) {
frame.setLong(getSlot(), value);
updateSlotKind(frame, FrameSlotKind.Long);
}
@Specialization(guards = "isDouble(value)")
protected void writeDouble(VirtualFrame frame, double value) {
frame.setDouble(getSlot(), value);
updateSlotKind(frame, FrameSlotKind.Double);
}
@Specialization(guards = "isBoolean(value)")
protected void writeBoolean(VirtualFrame frame, boolean value) {
frame.setBoolean(getSlot(), value);
updateSlotKind(frame, FrameSlotKind.Boolean);
}
@Specialization(replaces = {"writeLong", "writeDouble", "writeBoolean"})
protected void writeObject(VirtualFrame frame, Object value) {
frame.setObject(getSlot(), value);
updateSlotKind(frame, FrameSlotKind.Object);
}
private void updateSlotKind(VirtualFrame frame, FrameSlotKind kind) {
if (frame.getFrameDescriptor().getFrameSlotKind(getSlot()) != kind) {
CompilerDirectives.transferToInterpreter();
frame.getFrameDescriptor().setFrameSlotKind(getSlot(), kind);
}
}
protected boolean isLong(Object value) {
return value instanceof Long;
}
protected boolean isDouble(Object value) {
return value instanceof Double;
}
protected boolean isBoolean(Object value) {
return value instanceof Boolean;
}
@Override
public void executeVoid(VirtualFrame frame) {
Object value = valueNode.executeGeneric(frame);
executeWrite(frame, value);
}
protected abstract void executeWrite(VirtualFrame frame, Object value);
}
Control Flow Nodes
package com.example.tinylang.nodes.control;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
// If-Then-Else node
@NodeInfo(description = "If-then-else statement")
@NodeChildren({
@NodeChild("condition"),
@NodeChild("thenBranch"),
@NodeChild("elseBranch")
})
public abstract class IfNode extends ExpressionNode {
@Specialization
protected Object executeIf(boolean condition, Object thenValue, Object elseValue) {
return condition ? thenValue : elseValue;
}
}
// While loop node
@NodeInfo(description = "While loop")
@NodeChildren({
@NodeChild("condition"),
@NodeChild("body")
})
public abstract class WhileNode extends StatementNode {
@Specialization
protected void executeWhile(VirtualFrame frame, boolean condition, StatementNode body) {
while (condition) {
body.executeVoid(frame);
// Re-evaluate condition
condition = ((ExpressionNode) getCondition()).executeBoolean(frame);
}
}
protected abstract Node getCondition();
protected abstract Node getBody();
}
// Block node for statement sequences
@NodeInfo(description = "Block of statements")
public class BlockNode extends StatementNode {
@Children private final StatementNode[] statements;
public BlockNode(StatementNode[] statements) {
this.statements = statements;
}
@Override
public void executeVoid(VirtualFrame frame) {
for (StatementNode statement : statements) {
statement.executeVoid(frame);
}
}
}
// Function call node
@NodeInfo(description = "Function call")
public abstract class FunctionCallNode extends ExpressionNode {
private final String functionName;
@Children private final ExpressionNode[] argumentNodes;
public FunctionCallNode(String functionName, ExpressionNode[] argumentNodes) {
this.functionName = functionName;
this.argumentNodes = argumentNodes;
}
@Specialization
protected Object executeCall(VirtualFrame frame) {
// Look up function in global scope
Object function = getContext().getGlobalScope().getVariable(functionName);
if (function instanceof Callable) {
Object[] arguments = new Object[argumentNodes.length];
for (int i = 0; i < argumentNodes.length; i++) {
arguments[i] = argumentNodes[i].executeGeneric(frame);
}
return ((Callable) function).call(arguments);
}
throw new RuntimeException("Function not found: " + functionName);
}
private TinyLanguageContext getContext() {
return lookupContextReference(TinyLanguageContext.class).get();
}
}
Built-in Functions
package com.example.tinylang.builtins;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
// Print function
@NodeInfo(description = "Print built-in function")
public abstract class PrintFunction extends BuiltinFunction {
@Specialization
public Object print(Object value) {
System.out.println(value);
return value;
}
}
// Length function for strings and arrays
@NodeInfo(description = "Length built-in function")
public abstract class LengthFunction extends BuiltinFunction {
@Specialization
public long length(String str) {
return str.length();
}
@Specialization
public long length(Object[] array) {
return array.length;
}
}
// Type conversion functions
@NodeInfo(description = "To string built-in function")
public abstract class ToStringFunction extends BuiltinFunction {
@Specialization
public String toString(Object value) {
return String.valueOf(value);
}
}
@NodeInfo(description = "To number built-in function")
public abstract class ToNumberFunction extends BuiltinFunction {
@Specialization
public double toNumber(String str) {
try {
return Double.parseDouble(str);
} catch (NumberFormatException e) {
return 0.0;
}
}
@Specialization
public double toNumber(boolean bool) {
return bool ? 1.0 : 0.0;
}
@Specialization
public double toNumber(Object obj) {
return 0.0;
}
}
// Base class for built-in functions
public abstract class BuiltinFunction {
public abstract Object call(Object[] arguments);
protected void checkArgumentCount(Object[] arguments, int expected) {
if (arguments.length != expected) {
throw new RuntimeException("Expected " + expected + " arguments, got " + arguments.length);
}
}
}
Parser Implementation
package com.example.tinylang.parser;
import com.example.tinylang.TinyLanguage;
import com.example.tinylang.nodes.*;
import com.example.tinylang.nodes.expressions.*;
import com.example.tinylang.nodes.control.*;
import com.oracle.truffle.api.source.Source;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import java.util.ArrayList;
import java.util.List;
public class TinyLanguageParser {
public ExpressionNode parse(TinyLanguage language, Source source) {
try {
String code = source.getCharacters().toString();
TinyLanguageLexer lexer = new TinyLanguageLexer(CharStreams.fromString(code));
TinyLanguageParser parser = new TinyLanguageParser(new CommonTokenStream(lexer));
ParseTree tree = parser.program();
TinyLanguageVisitor visitor = new TinyLanguageVisitor(language);
return visitor.visit(tree);
} catch (Exception e) {
throw new RuntimeException("Parse error: " + e.getMessage(), e);
}
}
}
// ANTLR Visitor for AST construction
class TinyLanguageVisitor extends TinyLanguageBaseVisitor<ExpressionNode> {
private final TinyLanguage language;
public TinyLanguageVisitor(TinyLanguage language) {
this.language = language;
}
@Override
public ExpressionNode visitIntLiteral(TinyLanguageParser.IntLiteralContext ctx) {
long value = Long.parseLong(ctx.INT().getText());
return new IntLiteralNode(value);
}
@Override
public ExpressionNode visitDoubleLiteral(TinyLanguageParser.DoubleLiteralContext ctx) {
double value = Double.parseDouble(ctx.DOUBLE().getText());
return new DoubleLiteralNode(value);
}
@Override
public ExpressionNode visitStringLiteral(TinyLanguageParser.StringLiteralContext ctx) {
String text = ctx.STRING().getText();
// Remove quotes
String value = text.substring(1, text.length() - 1);
return new StringLiteralNode(value);
}
@Override
public ExpressionNode visitAddSub(TinyLanguageParser.AddSubContext ctx) {
ExpressionNode left = visit(ctx.expression(0));
ExpressionNode right = visit(ctx.expression(1));
if (ctx.op.getType() == TinyLanguageParser.ADD) {
return AddNodeGen.create(left, right);
} else {
return SubtractNodeGen.create(left, right);
}
}
@Override
public ExpressionNode visitMulDiv(TinyLanguageParser.MulDivContext ctx) {
ExpressionNode left = visit(ctx.expression(0));
ExpressionNode right = visit(ctx.expression(1));
if (ctx.op.getType() == TinyLanguageParser.MUL) {
return MultiplyNodeGen.create(left, right);
} else {
return DivideNodeGen.create(left, right);
}
}
@Override
public ExpressionNode visitParens(TinyLanguageParser.ParensContext ctx) {
return visit(ctx.expression());
}
@Override
public ExpressionNode visitComparison(TinyLanguageParser.ComparisonContext ctx) {
ExpressionNode left = visit(ctx.expression(0));
ExpressionNode right = visit(ctx.expression(1));
switch (ctx.op.getType()) {
case TinyLanguageParser.EQUAL:
return EqualNodeGen.create(left, right);
case TinyLanguageParser.LESS:
return LessThanNodeGen.create(left, right);
default:
throw new UnsupportedOperationException("Unknown comparison operator");
}
}
@Override
public ExpressionNode visitLogicalAnd(TinyLanguageParser.LogicalAndContext ctx) {
ExpressionNode left = visit(ctx.expression(0));
ExpressionNode right = visit(ctx.expression(1));
return LogicalAndNodeGen.create(left, right);
}
@Override
public ExpressionNode visitLogicalOr(TinyLanguageParser.LogicalOrContext ctx) {
ExpressionNode left = visit(ctx.expression(0));
ExpressionNode right = visit(ctx.expression(1));
return LogicalOrNodeGen.create(left, right);
}
@Override
public ExpressionNode visitIfExpression(TinyLanguageParser.IfExpressionContext ctx) {
ExpressionNode condition = visit(ctx.expression(0));
ExpressionNode thenBranch = visit(ctx.expression(1));
ExpressionNode elseBranch = visit(ctx.expression(2));
return IfNodeGen.create(condition, thenBranch, elseBranch);
}
@Override
public ExpressionNode visitFunctionCall(TinyLanguageParser.FunctionCallContext ctx) {
String functionName = ctx.ID().getText();
List<ExpressionNode> arguments = new ArrayList<>();
if (ctx.expressionList() != null) {
for (TinyLanguageParser.ExpressionContext argCtx : ctx.expressionList().expression()) {
arguments.add(visit(argCtx));
}
}
return new FunctionCallNode(functionName, arguments.toArray(new ExpressionNode[0]));
}
}
Language Interoperability
Polyglot Integration
package com.example.tinylang.interop;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
// Tiny Language array for interop
@ExportLibrary(InteropLibrary.class)
public class TinyLanguageArray implements TruffleObject {
private final Object[] elements;
public TinyLanguageArray(Object[] elements) {
this.elements = elements;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return elements.length;
}
@ExportMessage
boolean isArrayElementReadable(long index) {
return index >= 0 && index < elements.length;
}
@ExportMessage
Object readArrayElement(long index) throws UnsupportedMessageException {
if (!isArrayElementReadable(index)) {
throw UnsupportedMessageException.create();
}
return elements[(int) index];
}
}
// Foreign function interface
public class TinyLanguageFFI {
private final InteropLibrary interop = InteropLibrary.getFactory().createDispatched(3);
public Object callForeignFunction(Object function, Object... arguments) {
try {
if (interop.isExecutable(function)) {
return interop.execute(function, arguments);
}
throw new RuntimeException("Object is not executable");
} catch (UnsupportedMessageException e) {
throw new RuntimeException("Failed to execute foreign function", e);
}
}
public Object importFromJS(String code) {
try {
Context context = Context.getCurrent();
return context.eval("js", code);
} catch (Exception e) {
throw new RuntimeException("Failed to import from JavaScript", e);
}
}
public Object importFromPython(String code) {
try {
Context context = Context.getCurrent();
return context.eval("python", code);
} catch (Exception e) {
throw new RuntimeException("Failed to import from Python", e);
}
}
}
Performance Optimization
Specialization and Caching
package com.example.tinylang.nodes.optimized;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
// Optimized function call with caching
public abstract class OptimizedFunctionCallNode extends ExpressionNode {
private final String functionName;
@Children private final ExpressionNode[] argumentNodes;
public OptimizedFunctionCallNode(String functionName, ExpressionNode[] argumentNodes) {
this.functionName = functionName;
this.argumentNodes = argumentNodes;
}
@Specialization(limit = "3")
protected Object executeCached(VirtualFrame frame,
@Cached("createBinaryProfile()") ConditionProfile profile) {
Object function = lookupFunction();
if (profile.profile(function instanceof BuiltinFunction)) {
Object[] arguments = evaluateArguments(frame);
return ((BuiltinFunction) function).call(arguments);
}
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Function not found: " + functionName);
}
private Object lookupFunction() {
return getContext().getGlobalScope().getVariable(functionName);
}
private Object[] evaluateArguments(VirtualFrame frame) {
Object[] arguments = new Object[argumentNodes.length];
for (int i = 0; i < argumentNodes.length; i++) {
arguments[i] = argumentNodes[i].executeGeneric(frame);
}
return arguments;
}
private TinyLanguageContext getContext() {
return lookupContextReference(TinyLanguageContext.class).get();
}
}
Testing Framework
package com.example.tinylang.test;
import com.example.tinylang.TinyLanguage;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class TinyLanguageTest {
@Test
void testArithmeticOperations() {
String code = """
2 + 3 * 4
""";
Object result = execute(code);
assertEquals(14L, result);
}
@Test
void testStringConcatenation() {
String code = """
"Hello, " + "World!"
""";
Object result = execute(code);
assertEquals("Hello, World!", result);
}
@Test
void testIfExpression() {
String code = """
if (5 > 3) then "yes" else "no"
""";
Object result = execute(code);
assertEquals("yes", result);
}
@Test
void testFunctionCalls() {
String code = """
print("Testing")
length("hello")
""";
Object result = execute(code);
assertEquals(5L, result);
}
private Object execute(String code) {
try (Context context = Context.newBuilder()
.allowAllAccess(true)
.build()) {
Value result = context.eval("tinylang", code);
return result.as(Object.class);
}
}
}
// Benchmark tests
class TinyLanguageBenchmark {
@Test
void benchmarkFibonacci() {
String fibonacciCode = """
let fib = fn(n) {
if (n < 2) then n else fib(n - 1) + fib(n - 2)
};
fib(20)
""";
long startTime = System.nanoTime();
Object result = execute(fibonacciCode);
long endTime = System.nanoTime();
assertEquals(6765L, result);
System.out.printf("Fibonacci(20) executed in: %.2f ms%n",
(endTime - startTime) / 1_000_000.0);
}
private Object execute(String code) {
// Implementation same as above
return null;
}
}
Deployment and Integration
Maven Build Configuration
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> <compilerArgs> <arg>-Agraal.Dump=</arg> <arg>-Agraal.PrintGraph=Network</arg> </compilerArgs> </configuration> </plugin> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>0.9.22</version> <extensions>true</extensions> <executions> <execution> <id>build-native</id> <goals> <goal>build</goal> </goals> <phase>package</phase> </execution> </executions> </plugin> </plugins> </build>
Language Distribution
// Service provider configuration
// META-INF/services/com.oracle.truffle.api.TruffleLanguage
com.example.tinylang.TinyLanguage
// Language configuration
public class TinyLanguageConfig {
public static void main(String[] args) {
try (Context context = Context.newBuilder()
.allowAllAccess(true)
.option("tinylang.Home", System.getProperty("user.dir"))
.build()) {
// Execute Tiny Language code
Value result = context.eval("tinylang", "1 + 2 * 3");
System.out.println("Result: " + result.asInt());
}
}
}
This comprehensive Truffle Framework implementation demonstrates how to build a complete language from scratch, including parsing, AST construction, node specialization, built-in functions, interoperability, and performance optimization. The framework enables high-performance language implementations that can leverage GraalVM's JIT compilation for near-native performance.