com.oracle.truffle.sl.SLMain 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.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.math.BigInteger;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.debug.DebuggerTags;
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.vm.PolyglotEngine;
import com.oracle.truffle.api.vm.PolyglotEngine.Value;
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.nodes.SLTypes;
import com.oracle.truffle.sl.nodes.call.SLDispatchNode;
import com.oracle.truffle.sl.nodes.call.SLInvokeNode;
import com.oracle.truffle.sl.nodes.call.SLUndefinedFunctionException;
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.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.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, but no object model.
*
* 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 BigInteger} 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}.
*
- 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}.
*
*
*
* 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. There 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.
*
*/
public final class SLMain {
/**
* The main entry point. Use the mx command "mx sl" to run it with the correct class path setup.
*/
public static void main(String[] args) throws IOException {
Source source;
if (args.length == 0) {
source = Source.fromReader(new InputStreamReader(System.in), "").withMimeType(SLLanguage.MIME_TYPE);
} else {
source = Source.fromFileName(args[0]);
}
executeSource(source, System.in, System.out);
}
private static void executeSource(Source source, InputStream in, PrintStream out) throws IOException {
out.println("== running on " + Truffle.getRuntime().getName());
PolyglotEngine engine = PolyglotEngine.newBuilder().setIn(in).setOut(out).build();
assert engine.getLanguages().containsKey(SLLanguage.MIME_TYPE);
try {
Value result = engine.eval(source);
if (result == null) {
throw new SLException("No function main() defined in SL source file.");
} else if (result.get() != SLNull.SINGLETON) {
out.println(result.get());
}
} catch (UnsupportedSpecializationException ex) {
out.println(formatTypeError(ex));
} catch (SLUndefinedFunctionException ex) {
out.println(String.format("Undefined function: %s", ex.getFunctionName()));
}
engine.dispose();
}
/**
* Provides a user-readable message for run-time type errors. SL is strongly typed, i.e., there
* are no automatic type conversions of values. Therefore, Truffle does the type checking for
* us: if no matching node specialization for the actual values is found, then we have a type
* error. Specialized nodes use the {@link UnsupportedSpecializationException} to report that no
* specialization was found. We therefore just have to convert the information encapsulated in
* this exception in a user-readable form.
*/
public static String formatTypeError(UnsupportedSpecializationException ex) {
StringBuilder result = new StringBuilder();
result.append("Type error");
if (ex.getNode() != null && ex.getNode().getSourceSection() != null) {
SourceSection ss = ex.getNode().getSourceSection();
if (ss != null && ss.getSource() != null) {
result.append(" at ").append(ss.getSource().getShortName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn());
}
}
result.append(": operation");
if (ex.getNode() != null) {
NodeInfo nodeInfo = SLContext.lookupNodeInfo(ex.getNode().getClass());
if (nodeInfo != null) {
result.append(" \"").append(nodeInfo.shortName()).append("\"");
}
}
result.append(" not defined for");
String sep = " ";
for (int i = 0; i < ex.getSuppliedValues().length; i++) {
Object value = ex.getSuppliedValues()[i];
Node node = ex.getSuppliedNodes()[i];
if (node != null) {
result.append(sep);
sep = ", ";
if (value instanceof Long || value instanceof BigInteger) {
result.append("Number ").append(value);
} else if (value instanceof Boolean) {
result.append("Boolean ").append(value);
} else if (value instanceof String) {
result.append("String \"").append(value).append("\"");
} else if (value instanceof SLFunction) {
result.append("Function ").append(value);
} else if (value == SLNull.SINGLETON) {
result.append("NULL");
} else if (value == null) {
// value is not evaluated because of short circuit evaluation
result.append("ANY");
} else {
result.append(value);
}
}
}
return result.toString();
}
}