Skip to content

Script Runner (onion)

The onion command compiles and executes Onion source files directly in memory, without creating .class files.

Usage

onion [options] source files... [program arguments]

Options

-classpath <classpath>

Set the classpath for compilation and execution.

onion -classpath lib/mylib.jar MyScript.on

-encoding <encoding>

Specify the character encoding of source files.

onion -encoding UTF-8 MyScript.on

-maxErrorReports <count>

Limit the number of compilation errors reported.

onion -maxErrorReports 10 MyScript.on

--dump-ast

Print the parsed AST to stderr before running the script.

onion --dump-ast MyScript.on

--dump-typed-ast

Print a typed AST summary (classes, fields, methods) to stderr before running the script.

onion --dump-typed-ast MyScript.on

--profile-compile

Emit a compile profile before the script is executed.

onion --profile-compile MyScript.on

--profile-format <text|json>

Choose text or JSON profile output.

onion --profile-compile --profile-format json MyScript.on

--profile-output <target>

Write the compile profile to stderr, stdout, or a file path.

onion --profile-compile --profile-format json \
      --profile-output target/script-profile.json \
      MyScript.on

--warn <off|on|error>

Control warning reporting. error treats warnings as compilation errors.

onion --warn error MyScript.on

--Wno <codes>

Suppress specific warning categories by code or name.

onion --Wno W0001,unused-parameter MyScript.on

Program Arguments

Arguments after the source file(s) are passed to your program:

onion MyScript.on arg1 arg2 arg3

Access them in your code:

class MyScript {
  public:
    static def main(args :String[]): void {
      foreach arg :String in args {
        println("Argument: " + arg)
      }
    }
}

Entry Point

The script runner determines the entry point automatically:

1. Explicit Main Method

If a class has a main method, it's used as the entry point:

class MyProgram {
  public:
    static def main(args :String[]): void {
      println("Hello from main method")
    }
}

2. First Class with Main

If multiple classes have main methods, the first one is used:

class First {
  public:
    static def main(args :String[]): void {
      println("This will run")
    }
}

class Second {
  public:
    static def main(args :String[]): void {
      println("This won't run")
    }
}

3. Top-Level Declarations and Expressions

If there's no explicit main method, the first top-level declaration or expression is the entry point:

println("Hello, World!")

val x: Int = 10
println("x = " + x)

// These block elements execute immediately

Examples

Simple Script

hello.on:

println("Hello, World!")

Run:

$ onion hello.on
Hello, World!

With Arguments

greet.on:

class Greeter {
  public:
    static def main(args :String[]): void {
      if args.length > 0 {
        println("Hello, " + args[0] + "!")
      } else {
        println("Hello, stranger!")
      }
    }
}

Run:

$ onion greet.on Alice
Hello, Alice!

$ onion greet.on
Hello, stranger!

Quick Calculations

calc.on:

val a: Int = 10
val b: Int = 20
println("Sum: " + (a + b))
println("Product: " + (a * b))

Run:

$ onion calc.on
Sum: 30
Product: 200

File Processing

count_lines.on:

import {
  java.io.BufferedReader;
  java.io.FileReader;
}

class LineCounter {
  public:
    static def main(args :String[]): void {
      if args.length == 0 {
        println("Usage: onion count_lines.on <filename>")
        return
      }

      val filename: String = args[0]
      val reader: BufferedReader = new BufferedReader(
        new FileReader(filename)
      )

      var count: Int = 0
      var line: String = null
      while (line = reader.readLine()) != null {
        count = count + 1
      }

      reader.close()
      println("Lines: " + count)
    }
}

Run:

$ onion count_lines.on data.txt
Lines: 42

In-Memory Compilation

The onion command:

  1. Compiles source files to bytecode
  2. Loads classes into memory
  3. Executes the entry point
  4. No .class files are created

This is ideal for: - Quick scripts - Testing code snippets - Automation tasks - One-off programs

Multiple Source Files

Compile and run multiple files:

onion Main.on Utils.on Helper.on

All files are compiled together, and the entry point is determined from the first file.

Error Handling

Compilation Errors

$ onion bad_syntax.on
Error: Type mismatch at bad_syntax.on:5
Compilation failed

Runtime Errors

$ onion runtime_error.on
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at RuntimeError.main(runtime_error.on:10)

Comparison with onionc

Feature onion onionc
Creates .class files No Yes
Execution Immediate Requires java command
Use case Scripts, testing Production, libraries
Speed Fast for small programs Better for repeated runs
Distribution Requires source Can distribute .class/.jar

Scripting Best Practices

Shebang Line (Unix-like systems)

Make scripts executable:

hello.on:

#!/usr/bin/env onion
println("Hello from script!")

Make executable:

chmod +x hello.on
./hello.on

Error Messages

Provide helpful error messages:

class Script {
  public:
    static def main(args :String[]): void {
      if args.length < 2 {
        println("Error: Missing arguments")
        println("Usage: onion script.on <input> <output>")
        return
      }

      // Process arguments...
    }
}

Exit Codes

Return appropriate exit codes:

class Script {
  public:
    static def main(args :String[]): void {
      if args.length == 0 {
        System::exit(1)  // Error
      }

      // Success
      System::exit(0)
    }
}

Next Steps