com.oracle.truffle.sl.SLLanguage Maven / Gradle / Ivy
Show all versions of truffle-sl Show documentation
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.sl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Scope;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.debug.DebuggerTags;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.sl.builtins.SLBuiltinNode;
import com.oracle.truffle.sl.builtins.SLDefineFunctionBuiltin;
import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltin;
import com.oracle.truffle.sl.builtins.SLPrintlnBuiltin;
import com.oracle.truffle.sl.builtins.SLReadlnBuiltin;
import com.oracle.truffle.sl.builtins.SLStackTraceBuiltin;
import com.oracle.truffle.sl.nodes.SLEvalRootNode;
import com.oracle.truffle.sl.nodes.SLTypes;
import com.oracle.truffle.sl.nodes.access.SLReadPropertyCacheNode;
import com.oracle.truffle.sl.nodes.access.SLReadPropertyNode;
import com.oracle.truffle.sl.nodes.access.SLWritePropertyCacheNode;
import com.oracle.truffle.sl.nodes.access.SLWritePropertyNode;
import com.oracle.truffle.sl.nodes.call.SLDispatchNode;
import com.oracle.truffle.sl.nodes.call.SLInvokeNode;
import com.oracle.truffle.sl.nodes.controlflow.SLBlockNode;
import com.oracle.truffle.sl.nodes.controlflow.SLBreakNode;
import com.oracle.truffle.sl.nodes.controlflow.SLContinueNode;
import com.oracle.truffle.sl.nodes.controlflow.SLDebuggerNode;
import com.oracle.truffle.sl.nodes.controlflow.SLIfNode;
import com.oracle.truffle.sl.nodes.controlflow.SLReturnNode;
import com.oracle.truffle.sl.nodes.controlflow.SLWhileNode;
import com.oracle.truffle.sl.nodes.expression.SLAddNode;
import com.oracle.truffle.sl.nodes.expression.SLBigIntegerLiteralNode;
import com.oracle.truffle.sl.nodes.expression.SLDivNode;
import com.oracle.truffle.sl.nodes.expression.SLEqualNode;
import com.oracle.truffle.sl.nodes.expression.SLFunctionLiteralNode;
import com.oracle.truffle.sl.nodes.expression.SLLessOrEqualNode;
import com.oracle.truffle.sl.nodes.expression.SLLessThanNode;
import com.oracle.truffle.sl.nodes.expression.SLLogicalAndNode;
import com.oracle.truffle.sl.nodes.expression.SLLogicalOrNode;
import com.oracle.truffle.sl.nodes.expression.SLMulNode;
import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode;
import com.oracle.truffle.sl.nodes.expression.SLSubNode;
import com.oracle.truffle.sl.nodes.local.SLLexicalScope;
import com.oracle.truffle.sl.nodes.local.SLReadLocalVariableNode;
import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
import com.oracle.truffle.sl.parser.Parser;
import com.oracle.truffle.sl.parser.SLNodeFactory;
import com.oracle.truffle.sl.parser.Scanner;
import com.oracle.truffle.sl.runtime.SLBigNumber;
import com.oracle.truffle.sl.runtime.SLContext;
import com.oracle.truffle.sl.runtime.SLFunction;
import com.oracle.truffle.sl.runtime.SLFunctionRegistry;
import com.oracle.truffle.sl.runtime.SLNull;
/**
* SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as
* simple and clean as possible in order to help understanding the ideas and concepts of Truffle.
* The language has first class functions, and objects are key-value stores.
*
* SL is dynamically typed, i.e., there are no type names specified by the programmer. SL is
* strongly typed, i.e., there is no automatic conversion between types. If an operation is not
* available for the types encountered at run time, a type error is reported and execution is
* stopped. For example, {@code 4 - "2"} results in a type error because subtraction is only defined
* for numbers.
*
*
* Types:
*
* - Number: arbitrary precision integer numbers. The implementation uses the Java primitive type
* {@code long} to represent numbers that fit into the 64 bit range, and {@link SLBigNumber} for
* numbers that exceed the range. Using a primitive type such as {@code long} is crucial for
* performance.
*
- Boolean: implemented as the Java primitive type {@code boolean}.
*
- String: implemented as the Java standard type {@link String}.
*
- Function: implementation type {@link SLFunction}.
*
- Object: efficient implementation using the object model provided by Truffle. The
* implementation type of objects is a subclass of {@link DynamicObject}.
*
- Null (with only one value {@code null}): implemented as the singleton
* {@link SLNull#SINGLETON}.
*
* The class {@link SLTypes} lists these types for the Truffle DSL, i.e., for type-specialized
* operations that are specified using Truffle DSL annotations.
*
*
* Language concepts:
*
* - Literals for {@link SLBigIntegerLiteralNode numbers} , {@link SLStringLiteralNode strings},
* and {@link SLFunctionLiteralNode functions}.
*
- Basic arithmetic, logical, and comparison operations: {@link SLAddNode +}, {@link SLSubNode
* -}, {@link SLMulNode *}, {@link SLDivNode /}, {@link SLLogicalAndNode logical and},
* {@link SLLogicalOrNode logical or}, {@link SLEqualNode ==}, !=, {@link SLLessThanNode <},
* {@link SLLessOrEqualNode ≤}, >, ≥.
*
- Local variables: local variables must be defined (via a {@link SLWriteLocalVariableNode
* write}) before they can be used (by a {@link SLReadLocalVariableNode read}). Local variables are
* not visible outside of the block where they were first defined.
*
- Basic control flow statements: {@link SLBlockNode blocks}, {@link SLIfNode if},
* {@link SLWhileNode while} with {@link SLBreakNode break} and {@link SLContinueNode continue},
* {@link SLReturnNode return}.
*
- Debugging control: {@link SLDebuggerNode debugger} statement uses
* {@link DebuggerTags#AlwaysHalt} tag to halt the execution when run under the debugger.
*
- Function calls: {@link SLInvokeNode invocations} are efficiently implemented with
* {@link SLDispatchNode polymorphic inline caches}.
*
- Object access: {@link SLReadPropertyNode} uses {@link SLReadPropertyCacheNode} as the
* polymorphic inline cache for property reads. {@link SLWritePropertyNode} uses
* {@link SLWritePropertyCacheNode} as the polymorphic inline cache for property writes.
*
*
*
* Syntax and parsing:
* The syntax is described as an attributed grammar. The {@link Parser} and {@link Scanner} are
* automatically generated by the parser generator Coco/R (available from
* http://ssw.jku.at/coco/). The grammar contains semantic
* actions that build the AST for a method. To keep these semantic actions short, they are mostly
* calls to the {@link SLNodeFactory} that performs the actual node creation. All functions found in
* the SL source are added to the {@link SLFunctionRegistry}, which is accessible from the
* {@link SLContext}.
*
*
* Builtin functions:
* Library functions that are available to every SL source without prior definition are called
* builtin functions. They are added to the {@link SLFunctionRegistry} when the {@link SLContext} is
* created. Some of the current builtin functions are
*
* - {@link SLReadlnBuiltin readln}: Read a String from the {@link SLContext#getInput() standard
* input}.
*
- {@link SLPrintlnBuiltin println}: Write a value to the {@link SLContext#getOutput() standard
* output}.
*
- {@link SLNanoTimeBuiltin nanoTime}: Returns the value of a high-resolution time, in
* nanoseconds.
*
- {@link SLDefineFunctionBuiltin defineFunction}: Parses the functions provided as a String
* argument and adds them to the function registry. Functions that are already defined are replaced
* with the new version.
*
- {@link SLStackTraceBuiltin stckTrace}: Print all function activations with all local
* variables.
*
*/
@TruffleLanguage.Registration(id = SLLanguage.ID, name = "SL", version = "0.30", mimeType = SLLanguage.MIME_TYPE)
@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, StandardTags.ExpressionTag.class, DebuggerTags.AlwaysHalt.class})
public final class SLLanguage extends TruffleLanguage {
public static volatile int counter;
public static final String ID = "sl";
public static final String MIME_TYPE = "application/x-sl";
public SLLanguage() {
counter++;
}
@Override
protected SLContext createContext(Env env) {
return new SLContext(this, env, new ArrayList<>(EXTERNAL_BUILTINS));
}
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
Source source = request.getSource();
Map 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 = Parser.parseSL(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(request.getSource().getCharacters());
sb.append(";}");
Source decoratedSource = Source.newBuilder(sb.toString()).mimeType(request.getSource().getMimeType()).name(request.getSource().getName()).build();
functions = Parser.parseSL(this, decoratedSource);
}
RootCallTarget main = functions.get("main");
RootNode evalMain;
if (main != null) {
/*
* We have a main function, so "evaluating" the parsed source means invoking that main
* function. However, we need to lazily register functions into the SLContext first, so
* we cannot use the original SLRootNode for the main function. Instead, we create a new
* SLEvalRootNode that does everything we need.
*/
evalMain = new SLEvalRootNode(this, main, functions);
} else {
/*
* Even without a main function, "evaluating" the parsed source needs to register the
* functions into the SLContext.
*/
evalMain = new SLEvalRootNode(this, null, functions);
}
return Truffle.getRuntime().createCallTarget(evalMain);
}
/*
* Still necessary for the old SL TCK to pass. We should remove with the old TCK. New language
* should not override this.
*/
@SuppressWarnings("deprecation")
@Override
protected Object findExportedSymbol(SLContext context, String globalName, boolean onlyExplicit) {
return context.getFunctionRegistry().lookup(globalName, false);
}
@Override
protected boolean isVisible(SLContext context, Object value) {
return value != SLNull.SINGLETON;
}
@Override
protected boolean isObjectOfLanguage(Object object) {
if (!(object instanceof TruffleObject)) {
return false;
}
TruffleObject truffleObject = (TruffleObject) object;
return truffleObject instanceof SLFunction || truffleObject instanceof SLBigNumber || SLContext.isSLObject(truffleObject);
}
@Override
protected String toString(SLContext context, Object value) {
if (value == SLNull.SINGLETON) {
return "NULL";
}
if (value instanceof SLBigNumber) {
return super.toString(context, ((SLBigNumber) value).getValue());
}
if (value instanceof Long) {
return Long.toString((Long) value);
}
return super.toString(context, value);
}
@Override
protected Object findMetaObject(SLContext context, Object value) {
if (value instanceof Number || value instanceof SLBigNumber) {
return "Number";
}
if (value instanceof Boolean) {
return "Boolean";
}
if (value instanceof String) {
return "String";
}
if (value == SLNull.SINGLETON) {
return "Null";
}
if (value instanceof SLFunction) {
return "Function";
}
return "Object";
}
@Override
protected SourceSection findSourceLocation(SLContext context, Object value) {
if (value instanceof SLFunction) {
SLFunction f = (SLFunction) value;
return f.getCallTarget().getRootNode().getSourceSection();
}
return null;
}
@Override
public Iterable findLocalScopes(SLContext context, Node node, Frame frame) {
final SLLexicalScope scope = SLLexicalScope.createScope(node);
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
private SLLexicalScope previousScope;
private SLLexicalScope nextScope = scope;
@Override
public boolean hasNext() {
if (nextScope == null) {
nextScope = previousScope.findParent();
}
return nextScope != null;
}
@Override
public Scope next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Scope vscope = Scope.newBuilder(nextScope.getName(), nextScope.getVariables(frame)).node(nextScope.getNode()).arguments(nextScope.getArguments(frame)).build();
previousScope = nextScope;
nextScope = null;
return vscope;
}
};
}
};
}
@Override
protected Iterable findTopScopes(SLContext context) {
return context.getTopScopes();
}
public static SLContext getCurrentContext() {
return getCurrentContext(SLLanguage.class);
}
private static final List> EXTERNAL_BUILTINS = Collections.synchronizedList(new ArrayList<>());
public static void installBuiltin(NodeFactory extends SLBuiltinNode> builtin) {
EXTERNAL_BUILTINS.add(builtin);
}
}