diff graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java @ 13821:b16ec83edc73

Documentation and more refactoring of Simple Language
author Christian Wimmer <christian.wimmer@oracle.com>
date Wed, 29 Jan 2014 20:45:43 -0800
parents 7c418666c6c9
children 64c77f0577bb
line wrap: on
line diff
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java	Wed Jan 29 20:43:28 2014 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java	Wed Jan 29 20:45:43 2014 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,65 +23,223 @@
 package com.oracle.truffle.sl;
 
 import java.io.*;
+import java.math.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.sl.builtins.*;
+import com.oracle.truffle.sl.nodes.*;
+import com.oracle.truffle.sl.nodes.call.*;
+import com.oracle.truffle.sl.nodes.controlflow.*;
+import com.oracle.truffle.sl.nodes.expression.*;
+import com.oracle.truffle.sl.nodes.local.*;
 import com.oracle.truffle.sl.parser.*;
 import com.oracle.truffle.sl.runtime.*;
 
+/**
+ * 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.
+ * <p>
+ * 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.
+ * 
+ * <p>
+ * <b>Types:</b>
+ * <ul>
+ * <li>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.
+ * <li>Boolean: implemented as the Java primitive type {@code boolean}.
+ * <li>String: implemented as the Java standard type {@link String}.
+ * <li>Function: implementation type {@link SLFunction}.
+ * <li>Null (with only one value {@code null}): implemented as the singleton
+ * {@link SLNull#SINGLETON}.
+ * </ul>
+ * The class {@link SLTypes} lists these types for the Truffle DSL, i.e., for type-specialized
+ * operations that are specified using Truffle DSL annotations.
+ * 
+ * <p>
+ * <b>Language concepts:</b>
+ * <ul>
+ * <li>Literals for {@link SLBigIntegerLiteralNode numbers} , {@link SLStringLiteralNode strings},
+ * and {@link SLFunctionLiteralNode functions}.
+ * <li>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 <=}, >, >=.
+ * <li>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.
+ * <li>Basic control flow statements: {@link SLBlockNode blocks}, {@link SLIfNode if},
+ * {@link SLWhileNode while} with {@link SLBreakNode break} and {@link SLContinueNode continue},
+ * {@link SLReturnNode return}.
+ * <li>Function calls: {@link SLCallNode calls} are efficiently implemented with
+ * {@link SLAbstractDispatchNode polymorphic inline caches}.
+ * </ul>
+ * 
+ * <p>
+ * <b>Syntax and parsing:</b><br>
+ * 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 <a
+ * href="http://ssw.jku.at/coco/">http://ssw.jku.at/coco/</a>). 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}.
+ * 
+ * <p>
+ * <b>Builtin functions:</b><br>
+ * 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
+ * <ul>
+ * <li>{@link SLReadlnBuiltin readln}: Read a String from the {@link SLContext#getInput() standard
+ * input}.
+ * <li>{@link SLPrintlnBuiltin println}: Write a value to the {@link SLContext#getOutput() standard
+ * output}.
+ * <li>{@link SLNanoTimeBuiltin nanoTime}: Returns the value of a high-resolution time, in
+ * nanoseconds.
+ * <li>{@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.
+ * </ul>
+ */
 public class SLMain {
 
-    private static final Object[] NO_ARGUMENTS = new Object[0];
+    /**
+     * 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) {
+        if (args.length == 0) {
+            throw new SLException("SL source file must be specified as the first argument");
+        }
+        String fileName = args[0];
+        int repeats = 1;
+        if (args.length >= 2) {
+            repeats = Integer.parseInt(args[1]);
+        }
 
-    public static void main(String[] args) {
         SourceManager sourceManager = new SourceManager();
-        Source source = sourceManager.get(args[0]);
-        SLContext context = new SLContext(sourceManager, System.err);
-        run(context, source, System.out, 1);
+        Source source = sourceManager.get(fileName);
+        SLContext context = new SLContext(sourceManager, new BufferedReader(new InputStreamReader(System.in)), System.out);
+        run(context, source, System.out, repeats);
     }
 
+    /**
+     * Parse and run the specified SL source. Factored out in a separate method so that it can also
+     * be used by the unit test harness.
+     */
     public static void run(SLContext context, Source source, PrintStream logOutput, int repeats) {
         if (logOutput != null) {
             logOutput.println("== running on " + Truffle.getRuntime().getName());
         }
 
+        /* Parse the SL source file. */
         Parser.parseSL(context, source);
+        /* Lookup our main entry point, which is per definition always named "main". */
         SLFunction main = context.getFunctionRegistry().lookup("main");
         if (main.getCallTarget() == null) {
-            throw new SLException("No function main() found.");
+            throw new SLException("No function main() defined in SL source file.");
         }
 
-        if (logOutput != null) {
-            printScript(context, logOutput);
-        }
+        /* Change to true if you want to see the AST on the console. */
+        boolean printASTToLog = false;
+
+        printScript("before execution", context, logOutput, printASTToLog);
         try {
             for (int i = 0; i < repeats; i++) {
                 long start = System.nanoTime();
-                Object result = main.getCallTarget().call(null, new SLArguments(NO_ARGUMENTS));
+                /* Call the main entry point, without any arguments. */
+                try {
+                    Object result = main.getCallTarget().call(null, new SLArguments(new Object[0]));
+                    if (result != SLNull.SINGLETON) {
+                        context.getOutput().println(result);
+                    }
+                } catch (UnsupportedSpecializationException ex) {
+                    context.getOutput().println(formatTypeError(ex));
+                }
                 long end = System.nanoTime();
 
-                if (result != SLNull.INSTANCE) {
-                    context.getPrintOutput().println(result);
-                }
-                if (logOutput != null) {
-                    logOutput.printf("== iteration %d: %.3f ms\n", (i + 1), (end - start) / 1000000.0);
+                if (logOutput != null && repeats > 1) {
+                    logOutput.println("== iteration " + (i + 1) + ": " + ((end - start) / 1000000) + " ms");
                 }
             }
 
         } finally {
-            if (logOutput != null) {
-                printScript(context, logOutput);
-            }
+            printScript("after execution", context, logOutput, printASTToLog);
         }
     }
 
-    private static void printScript(SLContext context, PrintStream logOutput) {
+    /**
+     * Dumps the AST of all functions to the IGV visualizer, via a socket connection. IGV can be
+     * started with the mx command "mx igv". Optionally, also prints the ASTs to the console.
+     */
+    private static void printScript(String groupName, SLContext context, PrintStream logOutput, boolean printASTToLog) {
+        GraphPrintVisitor graphPrinter = new GraphPrintVisitor();
+        graphPrinter.beginGroup(groupName);
         for (SLFunction function : context.getFunctionRegistry().getFunctions()) {
-            if (function.getCallTarget() != null) {
-                logOutput.println("=== function " + function.getName());
-                NodeUtil.printTree(logOutput, function.getCallTarget().getRootNode());
+            RootCallTarget callTarget = function.getCallTarget();
+            if (callTarget != null) {
+                graphPrinter.beginGraph(function.toString()).visit(callTarget.getRootNode());
+
+                if (logOutput != null && printASTToLog) {
+                    logOutput.println("=== " + function);
+                    NodeUtil.printTree(logOutput, callTarget.getRootNode());
+                }
             }
         }
+        graphPrinter.printToNetwork(true);
+    }
+
+    /**
+     * 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.
+     */
+    private 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();
+            result.append(" at ").append(ss.getSource().getName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn());
+        }
+        result.append(": operation");
+        if (ex.getNode() != null && ex.getNode().getClass().getAnnotation(NodeInfo.class) != null) {
+            result.append(" \"").append(ex.getNode().getClass().getAnnotation(NodeInfo.class).shortName()).append("\"");
+        }
+        result.append(" not defined for");
+
+        String sep = " ";
+        for (Object value : ex.getSuppliedValues()) {
+            if (value != 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 {
+                    result.append(value);
+                }
+            }
+        }
+        return result.toString();
     }
 }