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:
parent
b12906e3bb
commit
80a2a73801
@ -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...
|
||||
|
@ -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)
|
||||
|
@ -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
188
chuck/step2_eval.ck
Normal 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();
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
10
chuck/types/subr/MalAdd.ck
Normal file
10
chuck/types/subr/MalAdd.ck
Normal 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());
|
||||
}
|
||||
}
|
10
chuck/types/subr/MalDiv.ck
Normal file
10
chuck/types/subr/MalDiv.ck
Normal 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());
|
||||
}
|
||||
}
|
10
chuck/types/subr/MalMul.ck
Normal file
10
chuck/types/subr/MalMul.ck
Normal 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());
|
||||
}
|
||||
}
|
10
chuck/types/subr/MalSub.ck
Normal file
10
chuck/types/subr/MalSub.ck
Normal 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());
|
||||
}
|
||||
}
|
9
chuck/types/subr/MalSubr.ck
Normal file
9
chuck/types/subr/MalSubr.ck
Normal file
@ -0,0 +1,9 @@
|
||||
public class MalSubr extends MalObject
|
||||
{
|
||||
"subr" => type;
|
||||
|
||||
fun MalObject call(MalObject args[])
|
||||
{
|
||||
return new MalObject;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user