make immutability default everywhere
This commit is contained in:
@@ -44,7 +44,6 @@ class FieldForms {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO make immutability the default
|
||||
static function varOrProperty(formName:String, position:Position, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
if (args.length < 2 || args.length > 3) {
|
||||
throw '$formName with $args at $position has wrong number of arguments';
|
||||
|
||||
@@ -63,6 +63,7 @@ class Macros {
|
||||
CallExp(Symbol("begin").withPos(args[0].pos), [
|
||||
CallExp(Symbol("deflocal").withPos(args[0].pos), [
|
||||
TypedExp("Any", uniqueVarSymbol).withPos(args[0].pos),
|
||||
MetaExp("mut").withPos(args[0].pos),
|
||||
Symbol("null").withPos(args[0].pos)
|
||||
]).withPos(args[0].pos),
|
||||
CallExp(Symbol("cond").withPos(args[0].pos), [
|
||||
|
||||
@@ -42,35 +42,55 @@ class SpecialForms {
|
||||
ENew(Helpers.parseTypePath(classType), args.slice(1).map(convert)).withContextPos();
|
||||
};
|
||||
|
||||
// TODO this isn't tested and doesn't give an arg length warning
|
||||
// TODO this doesn't give an arg length warning
|
||||
map["set"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
EBinop(OpAssign, convert(args[0]), convert(args[1])).withContextPos();
|
||||
};
|
||||
|
||||
// TODO this isn't tested, immutable-default, or DRY with (let... ) or (defvar... ) and doesn't give an arg length warning
|
||||
// TODO allow var bindings to destructure lists and key-value pairs
|
||||
function toVar(nameExp:ReaderExp, valueExp:ReaderExp, isFinal:Bool, convert:ExprConversion) {
|
||||
return {
|
||||
name: switch (nameExp.def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw '$nameExp should be a symbol or typed symbol';
|
||||
},
|
||||
type: switch (nameExp.def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
},
|
||||
isFinal: isFinal,
|
||||
expr: convert(valueExp)
|
||||
};
|
||||
}
|
||||
|
||||
// TODO this doesn't give an arg length warning
|
||||
map["deflocal"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
EVars([
|
||||
{
|
||||
name: switch (args[0].def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw 'first element of (deflocal... ) with $args should be a symbol or typed symbol';
|
||||
},
|
||||
type: switch (args[0].def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
},
|
||||
isFinal: false,
|
||||
expr: convert(args[1])
|
||||
}
|
||||
]).withContextPos();
|
||||
var valueIndex = 1;
|
||||
var isFinal = switch (args[1].def) {
|
||||
case MetaExp("mut"):
|
||||
valueIndex += 1;
|
||||
false;
|
||||
default:
|
||||
true;
|
||||
};
|
||||
EVars([toVar(args[0], args[valueIndex], isFinal, convert)]).withContextPos();
|
||||
};
|
||||
|
||||
// TODO refactor out EVar generation and allow var bindings to destructure lists and key-value pairs
|
||||
// TODO this doesn't have an arg length check
|
||||
map["let"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
var bindingList = switch (args[0].def) {
|
||||
var bindingListIndex = 0;
|
||||
// If the first arg of (let... ) is &mut, make the bindings mutable.
|
||||
var isFinal = switch (args[0].def) {
|
||||
case MetaExp("mut"):
|
||||
bindingListIndex += 1;
|
||||
false;
|
||||
default:
|
||||
true;
|
||||
};
|
||||
var bindingList = switch (args[bindingListIndex].def) {
|
||||
case ListExp(bindingExps) if (bindingExps.length > 0 && bindingExps.length % 2 == 0):
|
||||
bindingExps;
|
||||
default:
|
||||
@@ -79,24 +99,10 @@ class SpecialForms {
|
||||
var bindingPairs = bindingList.groups(2);
|
||||
var varDefs = [
|
||||
for (bindingPair in bindingPairs)
|
||||
{
|
||||
name: switch (bindingPair[0].def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw 'first element of binding pair $bindingPair should be a symbol or typed symbol';
|
||||
},
|
||||
type: switch (bindingPair[0].def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
},
|
||||
isFinal: true, // Let's give (let...) variable immutability a try
|
||||
expr: convert(bindingPair[1])
|
||||
}
|
||||
toVar(bindingPair[0], bindingPair[1], isFinal, convert)
|
||||
];
|
||||
|
||||
var body = args.slice(1);
|
||||
var body = args.slice(bindingListIndex + 1);
|
||||
if (body.length == 0) {
|
||||
throw '(let....) expression with bindings $bindingPairs needs a body';
|
||||
}
|
||||
|
||||
@@ -166,6 +166,10 @@ class BasicTestCase extends Test {
|
||||
Assert.equals(null, BasicTestCase.myCondFallthrough);
|
||||
}
|
||||
|
||||
function testSetAndDeflocal() {
|
||||
Assert.equals("another thing", BasicTestCase.mySetLocal());
|
||||
}
|
||||
|
||||
function testOr() {
|
||||
Assert.equals(5, BasicTestCase.myOr1);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,11 @@
|
||||
:String c "stuff"]
|
||||
(Assert.equals 5 a)
|
||||
(Assert.equals 6 b)
|
||||
(Assert.equals "stuff" c)))
|
||||
(Assert.equals "stuff" c))
|
||||
(let &mut [a "str1"]
|
||||
(Assert.equals "str1" a)
|
||||
(set a "str2")
|
||||
(Assert.equals "str2" a)))
|
||||
|
||||
(defvar myConstructedString (new String "sup"))
|
||||
|
||||
@@ -124,4 +128,9 @@
|
||||
(defvar myCondFallthrough (cond
|
||||
(false "not this")))
|
||||
|
||||
(defvar myOr1 (or null 5))
|
||||
(defvar myOr1 (or null 5))
|
||||
|
||||
(defun mySetLocal []
|
||||
(deflocal loc &mut "one thing")
|
||||
(set loc "another thing")
|
||||
loc)
|
||||
Reference in New Issue
Block a user