Code editing in Twocode

Twocode is a language designed for code generation. To compile Twocode, start an interpreter and load the program. You can edit the code in all possible ways, then transpile it to a simple Java-like program.

Here we explore the Code type and what one can do with it in the interpreter.

Functions

We define a function that takes in two arguments and always returns 5.


In [1]:
func f(a, b):
    return 5

f(1, 2)


Out[1]:
5

In [2]:
f


Out[2]:
func(a, b): return 5

The function's body is stored in its code attribute. The body is an AST (Abstract Syntax Tree), a parsed Code object.

We can view a Code object as source code, or as the raw syntax tree.


In [3]:
f.code


Out[3]:
return 5

In [4]:
f.code.tree()


Out[4]:
code:
.   stmt_return tuple_expr expr_term term_literal literal:
.   .   value: "5"
.   .   type: "integer"

Assigning code

We can change f's code, and we will use the macro syntax feature to do so.

macro stmt is the AST of stmt. macro 1 + 2 isn't 3, it's a 1 + 2 Code object.


In [5]:
f.code = macro return (a + b)

print(
    f.source(),
    f(1, 2),
sep="\n")


func(a, b): return (a + b)
3

Now the function works as if this was its body in the first place. The a and b identifiers are evaluated in its scope. return 1 + 2 returns 3.

Arguments

The function's arguments are Arg objects, stored under args.


In [6]:
var args = f.args

args


Out[6]:
[Arg("a"), Arg("b")]

Class editing

We define a simple class.


In [7]:
class C:
    var x:Int = 10
    func msg(s:String):
        print("in C.msg", s)

var fields = C.__fields__
fields


Out[7]:
["msg": <func (C,String)->()>, "x": Attr(type=Int, default_=macro 10)]

A class's __fields__ is a map from field names to Attr and Func objects.

The method has a hidden this:C prepended to its arguments.


In [8]:
C.msg


Out[8]:
func(this:C, s:String): print("in C.msg", s)

Adding methods

We take our function f, change its arguments to make it a method of C and give it a new body to use C's x attribute.


In [9]:
args = [Arg(name="this", type=C)] + args
f.args = args
f.code = (this -> (x * a + b)).code
fields["f"] = f
C.__fields__ = fields

C


Out[9]:
class:
    var x:Int = 10
    
    func f(a, b): return (x * a + b)
    func msg(s:String): print("in C.msg", s)

This is the new "source code" of C.


In [10]:
var c = C()
c.f(2, 3)


Out[10]:
23