4.8 KiB
Tutorial: provide run commands
Now that your language is installed you need to tell Riju how to run it. Here's an example for Dart:
main: "main.dart"
template: |
void main() {
print('Hello, world!');
}
run: |
dart main.dart
Note:
- The contents of
template
are put into themain
filename, andrun
is expected to run that file. - The
main
filename should follow existing conventions for your language, typicallymain.foo
wherefoo
is a standard file extension. If there's no standard file extension you can pick a reasonable-sounding one, likemain.beatnik
for Beatnik. You can use subdirectories (e.g.src/main.foo
) if needed, but this is pretty rare. - The
template
code should print exactly the textHello, world!
with a trailing newline to stdout, or as close to that as possible.
Compiled languages
If your language has a separate compilation step that produces a
binary or other intermediate artifact, you can add a separate
compile
command; for example:
main: "Main.java"
template: |
public class Main {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
compile: |
javac Main.java
run: |
java Main
There is no hard requirement on the names of intermediate files. In
the case of Java, the intermediate file is named Main.class
, with
the java
command appending the .class
part implicitly.
Languages with REPLs
If your language has the ability to run an interactive session, you
can expose that functionality via the repl
key. Here is the desired
behavior for languages with REPLs:
- The
repl
command will start an interactive session without any user code loaded - The
run
command will execute the user code inmain
, like before, and will then start an interactive session, preferably in the same environment (so variables are still in scope, for example)
Here is an example for Python (-u
is to prevent output buffering):
repl: |
python3 -u
main: "main.py"
template: |
print("Hello, world!")
run: |
python3 -u -i main.py
Preserving variable scope
In the Python example above, passing -i main.py
to python3
starts
an interactive session where main.py
is executed, and then any
variables defined in main.py
can be inspected at the REPL. This is
the desired state of operation. Many interactive languages provide
some command-line option(s) to achieve a similar effect, although they
may be a bit obscure. For example, in Node.js, you have to pass the
program as a string:
repl: |
node
main: "main.js"
template: |
console.log("Hello, world!");
run: |
node -e "$(< main.js)" -i
Or you may be able to abuse a "startup" or "rc" file that is loaded at
startup in a special way. For example, Haskell's interpreter evaluates
.ghci
specially at startup:
repl: |
rm -f .ghci
ghci
main: "Main.hs"
template: |
module Main where
main :: IO ()
main = putStrLn "Hello, world!"
run: |
(echo ':load Main' && echo 'main') > .ghci && ghci
In the case of shells, we often want to put the code itself into the startup file:
repl: |
SHELL=/usr/bin/zsh HOME="$PWD" zsh
input: |
expr 123 \* 234
main: ".zshrc"
template: |
echo "Hello, world!"
createEmpty: ""
run: |
SHELL=/usr/bin/zsh HOME="$PWD" zsh
In the above example, we passed createEmpty: ""
. To explain, the
default behavior is for user code to be written into main
immediately, even before the user has clicked Run. This is so that
certain language servers will work correctly. However, in cases where
we're abusing a startup file, it may not be appropriate. After all,
zsh
will load .zshrc
no matter what (note that repl
and run
are identical here). By passing createEmpty: ""
, we make it so that
Riju will write the provided string (i.e. ""
) into main
before
executing repl
. This ensures that user code is not run when
starting a REPL, but only when actually running.
Sometimes it's simply not possible to preserve variable scope from
user code when starting an interactive session. In that case, we just
start the interactive session separately. It's important to start the
interactive session regardless of whether or not the user code had an
error (so use ;
instead of &&
):
repl: |
zoem
main: "main.azm"
template: |
\inform{Hello, world!}
run: |
zoem -I main.azm; zoem
Required input
Sometimes languages really don't want to provide a way to run code noninteractively. In this case, you can include instructions about how the user can manually run the code:
repl: |
hhvm -a
input: |
print 123 * 234
main: "main.hack"
template: |
<<__EntryPoint>>
function main(): void {
echo "Hello, world!\n";
}
run: |
echo "Type 'r' at the debugger prompt to run the code" && hhvm -a main.hack