1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-13 01:43:50 +03:00

Implement step 2

This commit is contained in:
Vasilij Schneidermann 2016-04-29 20:40:11 +02:00
parent b12906e3bb
commit 80a2a73801
11 changed files with 289 additions and 5 deletions

View File

@ -56,10 +56,13 @@
of operation caused it (no stacktrace or anything)
- No user-definable exceptions, no mechanism to catch or throw them
(other than intentionally doing something illegal)
- This means that you must use C-style error checking by converting
the potentially erroneous functions into returning a status code
and mutating a reference passed to them as argument which is
highly weird in a otherwise Java-like language
- This means that you should use C-style error checking by
converting the potentially erroneous functions into returning a
status code and mutating a reference passed to them as argument
which is highly weird in a otherwise Java-like language
- An alternative is defining an error object (which belongs to the
same supertype as the other legal return values) and checking its
type by inspecting the user-tracked type field
- Other oddities
- strict distinction between assigning values and references with
two separate operators for them (`<<` for array append doesn't
@ -68,3 +71,10 @@
with the regular operator...
- `<<` on an `type[]` gives you a weird error as you need to use an
`type[0]` (and a `type[]` is merely a reference...)
- The compiler will find lots of mistakes for you, but cannot figure
out code branches not returning anything which means that return
type violations will blow up in your face unless there's a
reasonable default value (null for `Object` isn't, 0 for `int` and
"" for `string` is)
- If you abuse the type system too much, chances are you get a
segfault or assert instead of an exception...

View File

@ -13,3 +13,17 @@
`eval`-like thing or alternatively, chopping off the surrounding
quotes and doing the inverse transformation of the printing)
- How is an atom printed?
# Step 2
- What if my language doesn't support lambdas, let alone passing
around named functions? Ideally write something about
implementing/using functors/delegates or replacing that namespace
with a big switch as with VHDL. Another problem is that if you
choose a different solution in step 4, step 2 could end up no longer
functional...
- What kind of error (read: what message?) is raised when no value can
be looked up for the symbol? Is it arbitrary? Do I need to extend
my error handling to allow for format strings?
- It would be worth a mention that you should extend the printer to
handle "native" functions (or in oldtimey terms, subrs)

View File

@ -36,6 +36,10 @@ public class Printer
{
return "(atom " + pr_str((m$MalAtom).value(), print_readably) + ")";
}
else if( type == "subr" )
{
return "#<Subr>";
}
else if( type == "list" )
{
return pr_list((m$MalList).value(), print_readably, "(", ")");
@ -48,6 +52,10 @@ public class Printer
{
return pr_list((m$MalHashMap).value(), print_readably, "{", "}");
}
else
{
return "Unknown type";
}
}
fun static string pr_list(MalObject m[], int print_readably, string start, string end)

188
chuck/step2_eval.ck Normal file
View File

@ -0,0 +1,188 @@
// @import types/boxed/String.ck
// @import types/boxed/Int.ck
// @import types/mal/MalObject.ck
// @import types/mal/MalError.ck
// @import types/mal/MalAtom.ck
// @import types/mal/MalTrue.ck
// @import types/mal/MalFalse.ck
// @import types/mal/MalNil.ck
// @import types/mal/MalInt.ck
// @import types/mal/MalString.ck
// @import types/mal/MalSymbol.ck
// @import types/mal/MalKeyword.ck
// @import types/mal/MalList.ck
// @import types/mal/MalVector.ck
// @import types/mal/MalHashMap.ck
// @import types/subr/MalSubr.ck
// @import types/subr/MalAdd.ck
// @import types/subr/MalSub.ck
// @import types/subr/MalMul.ck
// @import types/subr/MalDiv.ck
// @import util/Status.ck
// @import reader.ck
// @import printer.ck
fun MalObject READ(string input)
{
return Reader.read_str(input);
}
fun MalObject EVAL(MalObject m, MalSubr env[])
{
if( m.type == "list" )
{
if( (m$MalList).value().size() == 0 )
{
return m;
}
eval_ast(m, env) @=> MalObject result;
if( result.type == "error" )
{
return result;
}
(result$MalList).value() @=> MalObject values[];
values[0]$MalSubr @=> MalSubr subr;
MalObject.slice(values, 1) @=> MalObject args[];
return subr.call(args);
}
else
{
return eval_ast(m, env);
}
}
fun MalObject eval_ast(MalObject m, MalSubr env[])
{
m.type => string type;
if( type == "symbol" )
{
(m$MalSymbol).value() => string symbol;
env[symbol] @=> MalSubr subr;
if( subr == null )
{
return MalError.create(Status.SYMBOL_NOT_FOUND, symbol);
}
else
{
return subr;
}
}
else if( type == "list" || type == "vector" || type == "hashmap" )
{
(m$MalList).value() @=> MalObject values[];
MalObject results[values.size()];
if( type != "hashmap" )
{
for( 0 => int i; i < values.size(); i++ )
{
EVAL(values[i], env) @=> MalObject result;
if( result.type == "error" )
{
return result;
}
result @=> results[i];
}
}
else
{
for( 0 => int i; i < values.size(); i++ )
{
if( i % 2 == 0 )
{
values[i] @=> results[i];
}
else
{
EVAL(values[i], env) @=> results[i];
}
}
}
if( type == "list" )
{
return MalList.create(results);
}
else if( type == "vector" )
{
return MalVector.create(results);
}
else if( type == "hashmap" )
{
return MalHashMap.create(results);
}
}
else
{
return m;
}
}
fun string PRINT(MalObject m)
{
return Printer.pr_str(m, true);
}
MalSubr repl_env[0];
new MalAdd @=> repl_env["+"];
new MalSub @=> repl_env["-"];
new MalMul @=> repl_env["*"];
new MalDiv @=> repl_env["/"];
fun string rep(string input)
{
READ(input) @=> MalObject m;
if( m.type == "error" )
{
return Status.toMessage(m$MalError);
}
EVAL(m, repl_env) @=> MalObject result;
if( result.type == "error" )
{
return Status.toMessage(result$MalError);
}
return PRINT(result);
}
fun void main()
{
ConsoleInput stdin;
string input;
while( true )
{
stdin.prompt("user>") => now;
stdin.getLine() => input;
rep(input) => string output;
if( output == "empty input" )
{
// proceed immediately with prompt
}
else
{
chout <= output + "\n";
}
}
}
main();

View File

@ -29,4 +29,16 @@ public class MalObject
return values;
}
fun static MalObject[] slice(MalObject objects[], int index)
{
MalObject values[objects.size() - index];
for( index => int i; i < objects.size(); i++ )
{
objects[i] @=> values[i - index];
}
return values;
}
}

View File

@ -0,0 +1,10 @@
public class MalAdd extends MalSubr
{
fun MalObject call(MalObject args[])
{
args[0]$MalInt @=> MalInt a;
args[1]$MalInt @=> MalInt b;
return MalInt.create(a.value() + b.value());
}
}

View File

@ -0,0 +1,10 @@
public class MalDiv extends MalSubr
{
fun MalObject call(MalObject args[])
{
args[0]$MalInt @=> MalInt a;
args[1]$MalInt @=> MalInt b;
return MalInt.create(a.value() / b.value());
}
}

View File

@ -0,0 +1,10 @@
public class MalMul extends MalSubr
{
fun MalObject call(MalObject args[])
{
args[0]$MalInt @=> MalInt a;
args[1]$MalInt @=> MalInt b;
return MalInt.create(a.value() * b.value());
}
}

View File

@ -0,0 +1,10 @@
public class MalSub extends MalSubr
{
fun MalObject call(MalObject args[])
{
args[0]$MalInt @=> MalInt a;
args[1]$MalInt @=> MalInt b;
return MalInt.create(a.value() - b.value());
}
}

View File

@ -0,0 +1,9 @@
public class MalSubr extends MalObject
{
"subr" => type;
fun MalObject call(MalObject args[])
{
return new MalObject;
}
}

View File

@ -4,6 +4,7 @@ public class Status
static int EMPTY_INPUT;
static int UNEXPECTED_TERMINATOR;
static int EXPECTED_TERMINATOR;
static int SYMBOL_NOT_FOUND;
static string status_codes[];
@ -31,8 +32,10 @@ public class Status
1 => Status.EMPTY_INPUT;
2 => Status.UNEXPECTED_TERMINATOR;
3 => Status.EXPECTED_TERMINATOR;
4 => Status.SYMBOL_NOT_FOUND;
["success",
"empty input",
"unexpected '%'",
"expected '%', got EOF"] @=> Status.status_codes;
"expected '%', got EOF",
"'%' not found"] @=> Status.status_codes;