make immutability default everywhere

This commit is contained in:
2020-11-25 11:32:04 -07:00
parent d60f62f9cb
commit dab730e3eb
5 changed files with 59 additions and 40 deletions

View File

@@ -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';

View File

@@ -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), [

View File

@@ -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';
}

View File

@@ -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);
}

View File

@@ -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)