castro compiles a simple "C" like language into Astrolog commands and AstroExpressions; castro is tailored to AstroExpression. castro is a standalone tool. It outputs a .as file that can be used with Astrolog's command switch -i <name>.as. castro generated *.as files easily interoperates with existing Astrolog command switch files.
Some motivating factors for castro
- familiar expression syntax (avoid writing and maintaining the prefix notation expressions),
- referring to things by name rather than address.
- automatic memory/address allocation
There's a cheat sheet
For those who like to play around before reading the docs: cheat sheet.
Building castro
See codegen.
Here's a simple example. Note that the switch, macro and variable definitions could be in 3
different files. As in Astrolog, function names are case insensitive;
following that convention Switch and macro names are case insensitive.
var yearA;
var yearB;
macro progressByYears {
yearA = 1973;
yearB = 1975;
Switch(progressedAspectsBetweenYearsAB);
}
// yearA/yearB inclusive
switch progressedAspectsBetweenYearsAB {
-dpY {~ yearA < yearB ? yearA : yearB; } {~ Abs(yearA - yearB) + 1; }
}
which generates
~M 1 '= 27 1973 = 28 1975 Switch 1'
-M0 1 '-dpY "~ IfElse Lt @27 @28 @27 @28" "~ Add Abs Sub @27 @28 1"'
This shows that castro is a thin layer that mirrors Astrolog and AstroExpression basics. See discussions for musings on possible extensions.
castro 0.9.x introduces macro functions. From the previous example the macro
can be defined as follows and the years are provided when the macro is
invoked. NOTE: There is no stack; in the example, yearA and yearB are
global variables, beware of recusion. Macro functions are syntactic sugar.
switch progressedAspectsBetweenYearsAB {
-dpY {~ yearA < yearB ? yearA : yearB; } {~ Abs(yearA - yearB) + 1; }
}
macro progressByYears(yearA, yearB) { // declares globals yearA/yearB
cprintf("Progressed aspects between years %d and %d\n", yearA, yearB);
Switch(progressedAspectsBetweenYearsAB);
}
run { ~1 { progressByYears(1973, 1975); } }
This generates the following
; SWITCH progressedAspectsBetweenYearsAB
-M0 49 '-dpY "~ IfElse Lt @27 @28 @27 @28" "~ Inc Abs Sub @27 @28"'
; MACRO progressByYears
~M 1 'Switch 50 Switch 49'
; RUN
~1 "Do2 = 27 1973 = 28 1975 Macro 1"
Note that the cprintf is in the generated helper.castro file.
; SWITCH castroHelperPrint_1
-M0 50 '~1 "= 29 @a = 30 @b
=a @27 =b @28" -YYT "Progressed aspects between years \A and \B\n"
~1 "=a @29 =b @30"'
For more examples, there is
- mazegame ported to
castro - expressionAsSwitchCommandParameter.castro
which describes in detail how to use an
AstroExpressionas a switch parameter incastro. - astroExpressionCommandSwitches.castro
has the "hook" examples from
Astrologwebsite ported tocastro. See:
AstroExpressions various command switches which allow one to define AstroExpression “hooks” - astrotest.d runs
castroto generate*.asfiles which are then executed on astrolog; it has a simple expect/result infrastructure - test.d checks lowlevel
castrofunctionality and has gold files. - several of these examples, and more, are in examples.d
castro can interoperate with existing command switch files and their switch/macro/variables.
castro has a layout directive which constrains automatically
allocated addresses to specified areas. In addition, it is possible to assign
an address to a name; this allows referencing items defined in an existing
command switch file (like an extern); use
switch a_switch @33; // in a non castro file there's: -M0 33 "..."
macro a_macro @50; // in a non castro file there's: ~M 50 "..."
var a_var @60;
To assign switch/macro in a castro file to a specific adress:
switch b_switch @44 { ... } // from a non-castro file: '-M 44' or '~1 "Switch 44"'
macro b_macro @55 { ... } // from a non-castro file: '~1 "Macro 55"'
When the address is specified, it may be a constant expression
const my_func_keys {33};
const yyy_base {50};
switch sw_01 @my_func_keys { ... } // assign to switch addr 33
switch sw_02 @my_func_keys + 1 { ... } // assign to switch addr 34
macro ma_01 @yyy_base + 7 { ... } // assign to macro addr 57
In a switch or run statement, castro has a weird looking cprintf output
command, see castro printf. And examples
cprintf.castro for a description that compiles and
runs. In a macro statement, cprintf looks "normal".
Macro functions use globals for function parameters. There is no stack; no recursion.
- Everything has a value,
if,while,do,for,repeat,{}, assignments, expressions as described in theAstrologdocumentation. - No semi-colons before
elseor before while indo while(). - No user defined functions, only builtin functions. There are
macrofunctions, but they are syntactic sugar. - Not all "C" operators are supported. The following are supported and have
"C" precedence and semantics
- unary ops:
+,-,!,~,*,& - arithmetic ops:
*,/,%,+,-,<<,>>&,^,| - relational/logical ops:
<,<=,>,>=,&&,|| - assignment ops:
=,+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|= - ternary:
?:
- unary ops:
- Address of and indirect,
&var_name,&arr_name[expr]and*var_namesupported; nothing more complex. - Integer constants are decimal, hex (
0x), binary(0b), octal(0o). - Floating constants are decimal
###.###, exponents not supported.
- All variables
varand contantsconstare part of a global namespace; there are no local variables. - Variable names are case insensitive.
- Single char variable names 'a' to 'z' are pre-declared.
AstroExpression hooks may use %u ... %z.
castro printf may use %a ... %j but by default
cprintfautomatically preserve variables. - Variables are declared with
var, for examplevar foo;; variables and const are initialized likevar foo {123};andconst bar {456};. - A variable is integer or float depending on usage.
Astrologpromotes/truncates as needed. - A variable declaration may assign the variable or array to a specific location; append
@integer, for examplevar foo @100;andvar bar[10] @200;; this assignsfooto location 100 and arraybarstarts at location 200.
Requires jre-11 or later. The released jar is executable, use a script named castro like
#!/bin/sh
CASTRO_JAR=/lib/castro-1.0.0.jar
java -jar $CASTRO_JAR "$@"
Do castro -h to see help/usage.
Running castro like castro file_name -o - is convenient to see the compiler output on
the console. The --fo=min option might be handy.
Running castro on a file produces 3 output files.
For example, if there's foo.castro then executing castro foo.castro creates
foo.ascan be executed withastrolog -i foo.as
or maybeastrolog -i foo.helper.asfoo.defhas details of allocation per filefoo.maphas a summary of allocation for all the files;
includes the file and line number where each item is defined
Use castro --gui ... or castro --console ... to see how a file is parsed.
When multiple files are compiled together, the .map base file name defaults to the first file in the input file list; use --mapname=base explicitly set the map file name base.
Compiling multiple files together resolves symbolic references between files. This example is available in castroCompileExample which is compiled and then run on astrolog by doing
castro --mapname test file1.castro file2.castro main.castro
astrolog -i test.helper.as
Then press the function keys, F1 through F4. Notice how F4 increments a number displayed when
F2 or F3 is pressed.
And see helper.castro to understand the
test.helper.as file in the above example.
This example shows how files compiled together reference items in each other.
Load order is important; the file, main.castro, is loaded last.
As main.castro is loaded, it invokes a macro function
(using the run statement) loaded in an earlier file.
main.castro also initializes a variable declared in file2.castro.
Examine the .def files to see the allocation details per file.
The file test.map shows the allocation for all the files, in this excerpt
var cprintf_save_area[10] @32; // [ALLOC] test.helper.castro:1
var f1m_param1 @27; // [ALLOC] file1.castro:36
var second_arg @30; // [ALLOC] file2.castro:5
note that each line has the file name in which the variable is declared.
To support cprintf(), printf() or string assignment in a macro and other
features, some helper
statements are automatically generated and placed in a
helper.castro file; the basename of the file is the same as the map file
basename. When compilation completes there is a basename.helper.as file.
The helper.castro file may have several types of statements
- load compiled files in order, e.g.
-i file1 -i file2
This is not present if the--nohelperloadoption is used.
The path, if any, as specified on the command line is used for loading.
Note that this may be a full path. - declare
cprintf_save_area
var cprintf_save_area[10]; // cprintf save/restore up to 10 variables.
This is not present if already defined or if there are no output statements. - string assignment switch statements to support string assignment in a marco
When something likesome_var = "string value;"is in a marco. - print switch statements to support print statements in a macro
When something likecprintf("FOO");"is in a macro. KeyCode()macro implmentations that return values according to running platform.
And note that, if not specified, a file's layout restrictions are inherited
from the first file on the command line. This means that the helper.castro
file has the same layout restrictions as the first file.
For example, with foo.castro and bar.castro do
castro foo.castro bar.castro
astrolog -i foo.helper.as
The basename of the helper.castro file is same as the map file, so
castro --mapname=test foo.castro bar.castro
astrolog -i test.helper.as
And there's a --helpername=some_name option.
In the previous examples there is no path associated with the files.
If astrolog is executed from a different
directory, use the astrolog -Yi option.
astrolog -Yi0 /full/path/to/compilation/directory -i test.helper.as
Alternatively, a path can be specified when the files are compiled
castro --mapname=test /full/path/foo.castro /full/path/bar.castro
# then from a different directory do
astrolog -i /full/path/test.helper.as
If the --nohelperload option is used, and there are macro output statements, then
castro --nohelperload foo.castro bar.castro
astrolog -i foo.helper.as -i foo.as -i bar.as
Note that when there is no macro output or string assignment
and the --nohelperload is used, then
a helper.castro file is not generated.
castro --nohelperload foo.castro bar.castro
astrolog -i foo.as -i bar.as
Some errors that castro reports, may in fact not be errors depending on the
targeted version of Astrolog or because the "programmer knows what they're
doing". There are command line options to treat specified errors as options; try
castro -h.
Probably the trickiest thing when writing castro programs is dealing with
switch versus macro; it's like having two languages. The switch format is
the familiar Astrolog command switch file. switch and run take almost free
form input, very little checking, and delcarative; and macro is strictly
parsed and procedural. switch has two mechanisms that embed macro like
procedures
AstroExpressioncommand switch hooks:~cmd { ... }AstroExpressionas a command switch argument-Xxx {~ ... }
These are the top level statements
constdeclares a constant.layoutdirectives constrain automatic allocation. The three spaces/regions arememory/macro/switch.layoutis optional and, if present, must be before anything else (exceptconstdeclarations).vardeclarations and initialization.macrodefinitions result in~MAstrologcommands.switchdefinitions result in-M0Astrologcommands.runresults in inline top level command switches. Parsed likeswitch, but not embedded in a-M0.copyliterally copies text to the output file with no interpretation or changes.
castro identifiers are the same as with "C", but case insensitive;
likewise operators have the same precedence as with "C";
blanks and newlines are whitespace.
layout statement specifies, on a per file basis, the addresses that automatic allocation can use for memory, macro, and switch addresses.
layout memory {
base 101;
limit 111; // 111 exclusive
reserve 104, 106:108; // 108 inclusive
}
The values in layout must be constant expressions.
This directive allows allocation of addresses between 101 inclusive and 111
exclusive; but not addresses 104, 106, 107, 108. If an out of memory error
occurs look at the .def output file for more information.
The layout from the first file may be inherited. If a file does not specify a layout, the layout from the first file is inherited.
Note: For Astrolog 770 the switch base is always at least 49;
which is after the function key slots.
The macro statement defines an AstroExpression macro using ~M; it contains
expressions with function calls. See
AstroExpressions; there are
a wide variety of function calls. The value of Macro(some_macro), or
some_macro()if it is macro function, is the value of the last statement/expr
in some_macro as defined by Astrolog.
A simple macro definition looks like
macro macName { ... }
A macro function definition looks like
macro macFunName(...) { ... }
A macro function definition is indicated by () after the macro name. There are zero or more parameters within the '()'. Each parameter becomes a named global. Macro function calls may nest only if the macro has one parameter. Different macros may nest. For example.
macro macFun1(macFun1_arg) { ... }
macro macFun2(macFun2_arg1, macFun2_arg2) { ... }
macro macFun2B(macFun2B_arg1, macFun2B_arg2) { ... }
run { ~1 {
macFun1(macFun1(x)); // OK - can nest if only one parameter
macFun2(macFun2(x, y), x); // ERROR - macros with two parameters can not nest
macFun2(macFun2B(x, y), macFun2B(u, v)); // OK - different macros
} }
if (expr)exprif (expr)exprelseexprrepeat (expr)exprwhile (expr)exprdoexprwhile (expr)for(var=expr;expr)expr- expr
?expr:expr {one or more expr, each terminated by a semi-colon}
Note that everything is an expression, including the flow control statements themselves.
The macro() and switch() functions take either an identifier, which is a macro or switch name respectively, or an expression which evaluates to an address. Expressions and function calls are as usual. Note the following
var pp;
macro m1 {
macro(100 + a); // Invoke the macro with address "100 + a"
pp = 100 + a;
macro(pp); // This generates an error, the identifier pp is not the name of a macro
macro(+pp); // This works because "+pp" is unambiguously an expression
}
In castro, e1 ? e2 : e3 has the same semantics as if(e1) e2 else e3 (and
"C") and only evaluates one of e2 or e3. This is different from
Astrolog's ? : operator which evaluates both e2 and e3. castro
provides a function QuestColon(e1, e2, e3) which has the Astrolog semantics.
The switch statement generates a command switch macro using -M0; it
contains Astrolog command switches and their arguments.
var aspect;
var orb;
var var_strings[3];
switch nameId @12 {
-zl "122W19:59" "47N36:35"
~1 { aspect = 7; orb = 2; }
-Ao {~ aspect; } {~ orb; }
SetString var_strings[0] "one" 'two' "three"
cprintf "aspect %d, orb %d, 1st string <%s>\n" {~ aspect; orb; &var_strings; }
}
All Astrolog commands that start with ~, except ~0, _~0, take an
AstroExpression as an argument; it is delineated with { and }. An
AstroExpression can be used as an argument to a command switch macro; it is
delineated by {~ and }. SetString is used to assign strings. ~2, ~20,
~M commands are not directly supported.
Note that @12 assigns 12 to the switch's address which binds it to F12;
see Function key slots.
If a switch address is not assigned, it will be allocated; use layout
to specify the allocation range. Astrolog versions after 760 support
command switch macro numbers outside of the function key range.
Both the functions cprintf and printf are available. The difference is what
Astrolog switch command is used.
castro function |
astrolog command |
note |
|---|---|---|
cprintf |
-YYT |
Popup formatted text string in current context. |
printf |
-YYt |
Output formatted text string in current context. |
There are two forms of each; one for switch and one for macro.
The macro form is available since Astrolog 770; this macro form uses
automatically generated helper switch statements to do the actual output;
see helper.castro for details.
format_string - %d, %i, %f, %g to print a number (they are equivelent).
%s to print a string, **use its address** as the arg.
arguments - One AstroExpression per format specifier.
- switch
cprintf "v1 %d, v2 %d, string %s\n" {~ 3 + 4; 7 + 4; &str_var; } - macro
cprintf("v1 %d, v2 %d, string %s\n", 3 + 4, 7 + 4, &str_var)
cprintf/printf use the lower memory locations for the arguments;
there are up to 10 arguments they are put into: %a, %b, %c, ..., %i, %j
for output.
The program values, before the cprintf, are optionally saved and restored.
If there is an array variable named cprintf_save_area it is used as the save
area. cprintf_save_area is automatically defined in
helper.castro if there are any cprintf/printf
statements in the program.
Nested use of cprintf will not restore reliably.
The contents of a run statement are parsed identically to a switch statement. The difference is that the run's switch commands are at the top level of the .as file and not embedded in a -M0; they are executed when the file is sourced as in -i file.
Note: A switch or macro must already be defined when invoked from a run statement; if not, undefined behavior.
The copy{LITERALLY_COPIED_TO_OUTPUT} statement literally copies text to the output file with no
interpretation or changes; the ultimate hack/workaround. This is needed because some things don't
parse correctly. All whitespace, including newlines, is copied as is. Use '\}' to include a '}'
in the output.
copy { -zl 121W57'26.9 37N17'28.2 ; Default location [longitude and latitude] }
In a run statement, the -zl params do not parse correctly.
It also provide a way to redefine a macro/switch.
All contants are part of the global namespace. So, for example, configuration constants can be
defined in one file, and used from other files. Constants can sometimes be used before they are
defined; exceptions are for the size of an array or to specify an address location using @.
Constants are defined like const <name> {<expr>}; where <expr> is an expression made up only
of integer constants. Program wide constants can be defined in a single file; and putting that
file first in compilation order avoids some issues. Constants take up no AstroExpression VM
storage, they exist only in the castro compiler.
const const_name1 {10};
const const_name2 {const_name1 + 23};
All variables are part of the global namespace, see Constants.
Variable declarations take on one of the following forms
var var_name1; // automatically allocate
var var_name2[4]; // automatically allocate
var var_name3 @100; // assign variable to address 100
var var_name4[4] @101; // assign array variable to addresses 101-104
const some_size {5};
const addr_base {110};
var var_name5 @addr_base;
var var_name6 @addr_base + 1; // address can be a constant expression
// both size and address can be a constant expression
var var_name7[some_size+3] @addr_base + 2;
Note that forward references in initialization expressions will use whatever value happens to be there.
var var1 {1}; // initialize automantically allocated variable
var var2 @100 {1}; // initialize variable assigned to addess 100
var var_array1[] { a+b, c+d }; // declare and initialize 2 element array
var var_array2[4] { a+b, c+d }; // 4 element array, initialize first two elements
Builtin variables are initialized like other variables; but their address can not be assigned.
Initialize variables with strings in variable declarations like:
var some_string { "string" };
var some_strings[] { "string1", "string2", "string3" };
Assign string programatically in macro {...} like:
var var0;
macro someMacro {
var0 = "string";
}
Set strings programatically in switch {...} or run {...} like:
var var1;
var var_array[4];
switch someSwitch {
SetString var_array[0] "one" "two" // assign string to var_array[0], var_array[1]
SetString var_array[3] "one" // assign string to var_array[3]
SetString var1 "one" // assign string to var1
}
Use SetString, setstring, AssignString, assignstring, SetStrings, setstrings, AssignStrings, or assignstrings.
Given this file: share.castro
var share[] { 0, 1, 2 };
run {
SetString share[0] "zero" "one" "two"
// share[1] references both a string and a number
cprintf "share[1]: %s - %d\n" {~ &share[1]; share[1]; }
}
Compile and run it:
$ castro share.castro
$ astrolog -i share.as
And see the output:
share[1]: one - 1
-
castrochecks for validAstroExpressionfunction names. There is no such check for valid switch commands; if that information becomes available,castrowill use it. -
Too long switch or macro don't fit in
Astrolog's parser; there is no "too large" error. There is often an apparently unrelated error message. Splitting it into two...Astrolog7.80 is expected to fix this. -
Some cases where blanks are significant
- The functions
=Objand=Houcan cause problems, for example
a==Obj(...)is parsed asa == Obj(...), writea= =Obj(...)for assignment.castroprovidesAssignObj(...)andAssignHou(...)as unambiguous alternates. {~starts anAstroExpressionwhose value is used as a parameter to acommand switch, for example=R {~ a+b; }. But{ ~ a+b; }, with a space between{and~, parses as{ (~a) + b; }.
- The functions
-
A
switchis almost free form text, except insideAstroExpressions as with
~cmd {...}and-cmd {~ ... }. If a command switch argument contains language special characters,
like the:in-zl "122W19:59" "47N36:35"quote the word as shown. -
;ends an expression. Beware a missing;. Considermacro ex1 { a += 3; -3; } // add 3 to "a", return -3. macro ex2 { a += 3 -3; } // add 0 to "a", does "a += (3 - 3)", return "a". macro ex3 { (a += 3) -3; } // add 3 to "a", subtract 3 from result, returns original "a"Use "--gui" to graphically see how expressions are parsed.
More complex examples
macro ex4 { repeat(3) { a += 1; }; -3; } // similar to ex1 macro ex5 { repeat(3) { a += 1; } -3; } // similar to ex3, but only weirdly soex5does{ a += 1; } -3;three times. The result of first two subtractions are thrown away, the last is the value of therepeat(3).A prefix notation predence/parenthesis free language can be nice and does have advantages.
macrodefines anAstroExpression macrowith~M.switchdefines acommand switch macrowith-M0.- Quoted string can not have embedded quotes of any type.
And see Flow Control Statements used in macro.
The last expression of a macro is the return value.
macro macroName { aspect = 7; orb = 2; } // returns 2
The ~ command switches take an AstroExpression as an argument.
switch switchName { ~1 { aspect = 7; orb = 2; } }
A regular switch command can take an AstroExpression value as a parameter, use {~ ... }.
switch switchName { -Ao {~ aspect; } {~ orb; }
Declare/initialize numeric/string variables
AstroExpression hook processings can use up to 6 variables: u, v, ..., z.
Varible names are case insensitive.
layout memory { base 101; limit 111; reserve 104, 106:108; }
Also can specify layout for switch/macro. limit is exclusive, all else inclusive
var a {123}; // init builtin variable a to 123
var var1 @30; // declare variable var1 assigned to specific location
var var2[3] {456, 789}; // declare var2 with 3 elements, init first two.
var some_strings[] { "string1", "string2", "string3" };
Some functions are part of the castro language. Except for the key code
functions these functions are treated as constants.
The return values for the key code functions are constant for a given
operating system and astrolog version.
| Function Name | Alias | usage ex | note |
|---|---|---|---|
| SwitchAdress | SAddr | SAddr(switchName) | The address of a switch |
| MacroAdress | MAddr | MAddr(macroName) | The address of a macro |
| KeyCode | KeyC | KeyC("a") | "a" is ascii val 97, takes range ' ' - '~' |
| Switch2KeyCode | Sw2KC | Sw2KC(switchName) | see ~XQ, ~WQ hook |
| SizeOf | ----- | SizeOf(varname) | the number of locations used by the variable |
For function keys see system key codes.
Note: KeyCode() in castro v1.1.0 is implemented as a helper macro; it
dynamically takes into account the running OS, linux-X11 or windows, and astrolog version.
nav_wasd_keys array values are determined dynamically when the program runs.
| platform | KeyC('w') | KeyC('a') | KeyC('s') | KeyC('d') |
|---|---|---|---|---|
| linux-X11 | 119 | 97 | 115 | 100 |
| Windows Astrolog 7.70 | 40084 | 40134 | 40284 | 40089 |
These are values that may be in the castro variable z in ~XQ or ~XW.
By default Astrolog associates switch commands at adresses 1 - 48 with function keys
// 'a' keypress is mapped to execute func_key_demo at slot for Shift-F1
switch func_key_demo @S_FK_F0 + 1 { ... }
run { ~XQ { if (z == KeyC("a")) z = Sw2KC(func_key_demo); } }
Alternatively
run { ~XQ { if (z == KeyC("a")) { Switch(func_key_demo); z = KeyCode(' '); } } }
Integer constants: 201, 0xc9, 0b11001001 0o311.
Symbolic constants are case insensitive.
User constants are define as const const_name {~10 + 1};.
Identifiers that start with M_, O_, A_, H_, S_, K_, W_, Z_
are AstrologConstants.
See Astrolog Constants for the constants in tabular form.
The entire constant name is not needed,
only 3 characters are required after the prefix;
in a few instances more characters are needed to disambiguate.
Except for K_ and Z_, castro checks for valid constants;
K_ and Z_ are passed through as is to Astrolog.
There are constants for dealing with keyboard input.
See castro Constants for the constants in tabular form and some examples.
See castro printf for details.
See cprintf for example usage.
For a file, testprint.castro
var cprintf_save_area[10]; // save area for cprintf temps, up to 10.
var str;
switch cpr {
SetString str "a string"
cprintf "%d %s\n" {~ x + y; &str; }
}
macro mcpr {
str = "different string";
cprintf("%d %s\n", x + y, &str) }
}
and run it like
astrolog -i testprint.helper.as -i testprint.as
Note that testprint.helper.as must be included before anything it contains is
executed. The helper file contains no direct execution commands. For example,
it can be last as long as none of it's helper switch statements are executed,
directly or indirectly, from the top level, through run or copy in any
*.as file. Safest to always have it first.
See helper.castro for details.
- Handle single file, out of normally multi-file, compilation. Uses something like the .map file as input; maybe a
--extern-fileoption. Not sure this is an essential feature. May be too confusing; just compile them all. - Warn if switch/macro used before defined in same file.
run { ~1 { switch(some_switch); } }
switch some_switch { -YYT "Boo\n" }
- Generate a
.xrefoutput file which lists vars with where they are used. - Handle parsing inside a
switch/runbetter so fewer words require quoting. - After pass1 build a dependency graph for undefined constants, try to resolve.
- Implement some stack functions in a user library.
- Implement stack frames and local variables and recursion.