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 {
|
static function varOrProperty(formName:String, position:Position, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||||
if (args.length < 2 || args.length > 3) {
|
if (args.length < 2 || args.length > 3) {
|
||||||
throw '$formName with $args at $position has wrong number of arguments';
|
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("begin").withPos(args[0].pos), [
|
||||||
CallExp(Symbol("deflocal").withPos(args[0].pos), [
|
CallExp(Symbol("deflocal").withPos(args[0].pos), [
|
||||||
TypedExp("Any", uniqueVarSymbol).withPos(args[0].pos),
|
TypedExp("Any", uniqueVarSymbol).withPos(args[0].pos),
|
||||||
|
MetaExp("mut").withPos(args[0].pos),
|
||||||
Symbol("null").withPos(args[0].pos)
|
Symbol("null").withPos(args[0].pos)
|
||||||
]).withPos(args[0].pos),
|
]).withPos(args[0].pos),
|
||||||
CallExp(Symbol("cond").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();
|
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) -> {
|
map["set"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||||
EBinop(OpAssign, convert(args[0]), convert(args[1])).withContextPos();
|
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) -> {
|
map["deflocal"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||||
EVars([
|
var valueIndex = 1;
|
||||||
{
|
var isFinal = switch (args[1].def) {
|
||||||
name: switch (args[0].def) {
|
case MetaExp("mut"):
|
||||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
valueIndex += 1;
|
||||||
name;
|
false;
|
||||||
default:
|
default:
|
||||||
throw 'first element of (deflocal... ) with $args should be a symbol or typed symbol';
|
true;
|
||||||
},
|
};
|
||||||
type: switch (args[0].def) {
|
EVars([toVar(args[0], args[valueIndex], isFinal, convert)]).withContextPos();
|
||||||
case TypedExp(type, _):
|
|
||||||
Helpers.parseComplexType(type);
|
|
||||||
default: null;
|
|
||||||
},
|
|
||||||
isFinal: false,
|
|
||||||
expr: convert(args[1])
|
|
||||||
}
|
|
||||||
]).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) -> {
|
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):
|
case ListExp(bindingExps) if (bindingExps.length > 0 && bindingExps.length % 2 == 0):
|
||||||
bindingExps;
|
bindingExps;
|
||||||
default:
|
default:
|
||||||
@@ -79,24 +99,10 @@ class SpecialForms {
|
|||||||
var bindingPairs = bindingList.groups(2);
|
var bindingPairs = bindingList.groups(2);
|
||||||
var varDefs = [
|
var varDefs = [
|
||||||
for (bindingPair in bindingPairs)
|
for (bindingPair in bindingPairs)
|
||||||
{
|
toVar(bindingPair[0], bindingPair[1], isFinal, convert)
|
||||||
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])
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
var body = args.slice(1);
|
var body = args.slice(bindingListIndex + 1);
|
||||||
if (body.length == 0) {
|
if (body.length == 0) {
|
||||||
throw '(let....) expression with bindings $bindingPairs needs a body';
|
throw '(let....) expression with bindings $bindingPairs needs a body';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,6 +166,10 @@ class BasicTestCase extends Test {
|
|||||||
Assert.equals(null, BasicTestCase.myCondFallthrough);
|
Assert.equals(null, BasicTestCase.myCondFallthrough);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSetAndDeflocal() {
|
||||||
|
Assert.equals("another thing", BasicTestCase.mySetLocal());
|
||||||
|
}
|
||||||
|
|
||||||
function testOr() {
|
function testOr() {
|
||||||
Assert.equals(5, BasicTestCase.myOr1);
|
Assert.equals(5, BasicTestCase.myOr1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,11 @@
|
|||||||
:String c "stuff"]
|
:String c "stuff"]
|
||||||
(Assert.equals 5 a)
|
(Assert.equals 5 a)
|
||||||
(Assert.equals 6 b)
|
(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"))
|
(defvar myConstructedString (new String "sup"))
|
||||||
|
|
||||||
@@ -124,4 +128,9 @@
|
|||||||
(defvar myCondFallthrough (cond
|
(defvar myCondFallthrough (cond
|
||||||
(false "not this")))
|
(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