JotaCode Compiler

Inspired by Andrew Schepler's JotaCode Builder Language, I've created my own JotaCode compiler.

The compiler itself is a TADS 3-program (27,3 kB) and requires a recent TADS 3-interpreter, as described on my games-page.

You can also download the compiler source code and compile it yourself.

(No experience with TADS 3 development is needed to use JCC. TADS 3 just happens to be a language I like, and JCC could just as well have been written in Perl, C++ or Pascal.)

JCC can be run with no parameters, in which case it'll allow you to type code and have it compiled "live" (that is, as you enter the code.) Alternately, you can specify an input file. Your interpreter settings might have to be adjusted to allow JCC to access the file.

Sample use: t3run -plain jcc.t3 mycode.jc mycode.out
Or: t3run -plain jcc.t3 mycode.jc CON: to display compiled JotaCode on screen (using DOS/Windows).

You can now (again) try the JotaCode Compiler online. */?>

Known problems/to-do list

There's always room for improvements.

Sample code

headlessFace: thing #14125
{
    name = "-";
    name0 = "faceless head;face;faceless;head;flh;fh;headless;hlf;hf";
    name1 = "headless face;face;faceless;head;flh;fh;headless;hlf;hf";

    description = {
        let "x" = .toggle;
        .toggle = !%x;
        .name = .name[%x];
        "Wait... isn't it actually a "; shortname(this); "?";
    };
};

That code will result in exactly this output from the compiler (sans linewrapping):

@create - [as #14125]
@field #14125=name0:faceless head;face;faceless;head;flh;fh;headless;hlf;hf
@field #14125=name1:headless face;face;faceless;head;flh;fh;headless;hlf;hf
@desc #14125=@let("x",@g("toggle"),@print(
  @s("toggle",@not("%x")),
  @s("name",@g("name%x")),
  "Wait... isn't it actually a ",@shortname(14125),"?"
))

You can download the JCC-code for my miscellaneous MUD-code, or the JCC-code for the werewolf test.

Documentation

Yes, the JotaCode Compiler sure lacks proper documentation. Sorry about that. Here are examples of some of the language features. Also have a look at the sample code above.

Operators and special values

+, -, *, /, ( ), ! work as you'd expect.

Use { a; b; c; } or a & b & c to concatenate strings.

and and &&, or and || works too, and so does xor.

Compare values using ==, !=, <, >, <= and >=.

For integer division and modulos, use div and mod (/ performs floating-point division.)

Use has and hasnt to test flags, as in #123 has "male".

Use is and isnt to test type or location of an object, as in #123 is player, %x is on %y, #123 isnt in #14.

JCC also has cond ? if_true : if_false, which is exactly the same as if (cond) if_true; else if_false; - as all statements are valid expressions.

++, --, += and the like, and , as an operator (as in .foo = stuff(), %x;) are NOT supported.

varobj refers to the same object as @g and @s uses (i.e. "%!", unless "%!" defines a "varobj" field.)

this refers to the object currently being defined (using the object keyword.) Using this outside an object definition causes a compiler error.

foo.bar refers to the field "bar" on object #foo.

foo.bar[123] refers to the field "bar123" on object #foo.

.foo equals varobj.foo.

foo.bar() (or .bar()) @calls the field "bar" on #foo (or varobj).

Language features

{ let "x" = @location("%#"); "You're in #%x!"; };
@let("x",@location("%#"),"You're in #%x!")
contentsloop ("o" in #14) { "In the lounge there is "; @shortname("%o"); "%c"; };
@contentsloop(14,"o",@print("In the lounge there is ",@shortname("%o"),"%c"))
fieldloop (#1234 by "a_") "Field is %f, value is %v";
@fieldloop(1234,"a_","Field is %f, value is %v")
strloop ("x" in "hello world") if (%x == "l") "Boo!";
@strloop("hello world","x",@switch(1,@eq("%x",l),"Boo!"))

(I'm perfectly well aware of the non-optimized code there, and I'm working on it.)

const repeat_string = #14468.longstring;    repeat (12345.cnt) "Foo!";
@strloop(@substr(@getfield(14468,"longstring"),0,@getfield(12345,"cnt")),"repeat_","Foo!")
switch (expr) { case 1, 2, 3: ...; case "xyz": ...; default: ...; }
@switch(expr,{1,2,3},...,"xyz",...,...)
say "Boo!"; osay "Moo!"; rsay "Ook!";
@print( @tell("%#","Boo!"),
        @tellroom(@location("%#"),"%#","Moo!"),
        @tellroom(@location("%#"),"","Ook!") )

Object definitions

The object definitions can be written in a dozen of different styles. Pick the one you like.

myFooBar: thing #1234 in #475 has "jumpok"
{
    name = "foobar";
    name = "foo" & 'bar';
    name: "foobar";
    name() = "foobar";
    name() = { "foo"; "bar"; };
    name() { "foobar"; };
};

The complete syntax is: ([] means optional part, []* means repeatable part.)

[ identifier: ] (thing|room|action|exit|player) object_ID [(in|on) location] [has flag1 [, flag2]*]
{
   [ (field|"field") [( (0+ parameters))] [:|=] statement ; ]*
};

A statement can be a simple value or a statement-block in braces. Note that action and exit is exactly the same thing.

JCC also provides a special syntax for defining arrays on objects:

calendar: thing #12345
{
    name = "calendar";
    wd[] = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    description() { "Today is "; .wd[time("wday")]; ". The week has "; arraylen("wd"); " days. "; };
};

Note that arraylen is a compiler construct, that it's always evaluated at compile-time, and that it can only be used for arrays defined using the special array definition.

Also note that arraylen("wd") is the same as arraylen(this.wd), and that the latter syntax can be used to retrieve lengths of arrays defined for other objects.

Alternate syntax

JCC provides several minor variations in syntax for many of its constructs.

if (foo) bar; can be written simply as if foo bar;, if you don't mind confusing code, or as if foo then bar;

Generally, parentheses can be left out in many constructs:
switch foo { ... };
contentsloop "o" in #14 { ... };
strloop "x" in "12345" { ... };
fieldloop #1234 by "a_" { ... };

const-values don't actually have to be constant, and you can list more than one definition after const:
const player = "%#", playerloc = @location(player); @tellroom(playerloc, -1, "BAH!");
@tellroom(@location("%#"),-1,"BAH!")

Numbers can optionally be prefixed with a #.

Standard JotaCode can be used just as well as the special syntax for switch, contentsloop, etc.

varobj.x can be written simply as .x (and compiles to @g("x").)

"%#" can be written simply as %#, and "%{stuff}" can be written as %stuff.