I've been writing a neural network from scratch to learn from.
but since i'm still learning - I want to make sure what I'm writing is actually correct.
I have an array of arrays (a matrix), of cell objects. attached to a 'brain' object which has the following method two methods:
train: function(data)
{
for (let b=0; b< data.length; b++)// for the length of the training data - I.E. we are going assume we are getting many relatively shortly indexed arrays
{
if(data[b].answers.length != data[b].questions.length)
{
console.log("bad data");
return false;
}
for(let c=0;c<data[b].questions.length;c++)
{
brain.attachInputLayer(data[b].questions[c]);
brain.updateForward();
let direction = brain.determinDirection(data[b].answers[c]); //return an array of updateObject with determined weights bias value adjustments, which each cell gets updated order should be from generation by column;
brain.cellMatrix.forEach(cellArray=> cellArray.forEach(cell=> cell.adjust(direction.find(x=> x.ID ===cell.ID))));
brain.updateForward();
brain.displayBrain();
}
}
console.log("all training data done");
alert("win?");
console.log(brain.cellMatrix);
console.log("brain");
}
and
determinDirection:function(answer)
{
// answer is the array of values of each answer cell we want as a result
let arrayOfUpDateObjectsForCell = [];
for(let e=0; e<answer.length; e++)
{
let answerCell = brain.cellMatrix[cellMatrix.length-1][e];
let returnBucket = [];
arrayOfUpDateObjectsForCell.push(answerCell.whatIwant(answer[e], returnBucket));
}
let list = Flat(arrayOfUpDateObjectsForCell);
let realList = Clean(list);
return realList;
}
so each cell of the last generation (the answer output) calls the whatIwant method at brain.train(), this function propagates backwards through the network... but my question really is this:::
:::
does it look like I am calculating the error / direction to move each value correctly?
is averaging the changes between the duplicated updateObjects correct?
(the desiredObjectchange for cell.gen=3,order=0 gets created from each of the next layer cells calling whatIwant. the changes cell.gen=4,order=0 wants cell.gen=3,order=0 to have is averaged with the changes cell.gen=4,order=1 wants for cell.gen=3,order=0).
is averaging the correct operation here?
:::
whatIwant:function(answerValue, returnArray)
{
let desiredWeights = this.weights;
let desiredBias = this.bias;
let desiredActivations = this.activations;
let error = (1/2)*Math.pow(cell.value-answerValue,2);
let desiredObjectChange =
{
ID:this.ID,
weights:this.weights,
bias:this.bias,
activations:this.activations,
value:answerValue,
combine:function(yourCloneFriend)
{
if(yourCloneFriend == false)
{
return;
}
this.bias = (1/2)*(this.bias+yourCloneFriend.bias);
let cWeight = yourCloneFriend.weights[Symbol.iterator]();
let cActivations = yourCloneFriend.activations[Symbol.iterator]();
this.weights.forEach(x=> (1/2)*(x+cWeight.next().value));
this.activations.forEach(y=> (1/2)*(y+cActivations.next().value));
this.recalculateValue();
return this;
},
recalculateValue:function()
{
this.value = Sigmoid(arrayMultiply(this.weights, this.activations)+this.bias);
}
}
for(let k = 0; k< this.weights.length; k++)
{
let lastValue = Sigmoid(arrayMultiply(desiredWeights, desiredActivations)+desiredBias);
let lastError = (1/2)*Math.pow(lastValue-answerValue,2);
for(let l=0;l<3;l++)
{
let currentValue = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
let currentError = (1/2)*Math.pow(currentValue-answerValue,2);
let positiveRange = false;
if(desiredWeights[k] < 0){ positiveRange = true;}
let nudgedWeightArray = NudgeArray(desiredWeights, k, l, positiveRange); //returns a copy array to test, with weight adjusted.
let testWeightChange = Sigmoid(arrayMultiply(nudgedWeightArray,desiredActivations)+desiredBias);
let testWeightError = (1/nudgedWeightArray.length)*Math.pow(testWeightChange - answerValue, 2);
let testWeightResult = compareSmallnumbers('isSmaller', currentError, testWeightError);
if(testWeightResult);
{
desiredWeights = nudgedWeightArray;
currentError = testWeightError;
}
positiveRange=false;
if(desiredBias < 0){positiveRange = true;}
let nudgedBiasVal = this.nudge(desiredBias,l,positiveRange);
let testBiasChange = Sigmoid(nudgedBiasVal+desiredWeights[k]*desiredActivations[k]);
let testBiasError = (1/1)*Math.pow(testBiasChange - answerValue, 2);
let testBiastResult = ('isSmaller', currentError, testBiasError);
if(testBiastResult);
{
desiredBias = nudgedBiasVal;
currentError = testBiasError;
}
positiveRange=!!Math.random(0,1)>5;
let nudgedAcitivationArray = NudgeArray(desiredActivations,k,l,positiveRange);
let testActivationChange = Sigmoid(arrayMultiply(nudgedAcitivationArray,desiredWeights)+desiredBias);
let testActivationError = (1/nudgedAcitivationArray.length)*Math.pow(testActivationChange - answerValue, 2);
let testActivationResult = compareSmallnumbers('isSmaller', currentError, testActivationError);
if(testActivationResult);
{
desiredActivations[k] = nudgedAcitivationArray[k];
currentError = testActivationError;
}
//and the end of the loop, update the error to the new value
let errorResult = compareSmallnumbers('isSmaller',lastError, currentError);
if(errorResult)
{
lastError = currentError;
}
}
desiredObjectChange.weights[k] = desiredWeights[k];
desiredObjectChange.bias = desiredBias;
desiredObjectChange.activations[k] = desiredActivations[k];
desiredObjectChange.value = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
}
let combineObject = returnArray.find(x=>x.ID === desiredObjectChange.ID);
if(!combineObject)
{
returnArray.push(desiredObjectChange);
}
//that was this object - simple stuff, now we need to call this function
if(Array.isArray(cell.lastGenerationTargetKeys) && cell.lastGenerationTargetKeys.length)
{
let nextActivation = desiredObjectChange.activations[Symbol.iterator]();
brain.cellMatrix[cell.generation-1].forEach(x=> x.whatIwant(nextActivation.next().value, returnArray));
return returnArray;
}
else
{
return;
}
},
clean,flat and NudgeArray are these::
function Clean(array)
{
let rArray = [];
array.forEach((x)=>
{
let search = rArray.find(y=>y.ID ===x.ID);
if(search === undefined)
{
rArray.push(x);
}
else
{
rArray[rArray.indexOf(search)].combine(x);
}
});
return rArray;
}
function Flat(array)
{
let holdBucket = [];
let flatten = function(array)
{
for(let i = 0; i<array.length;i++)
{
if(Array.isArray(array[i]))
{
flatten(array[i]);
}
else
{
holdBucket.push(array[i]);
}
}
}
flatten(array);
return holdBucket;
}
function NudgeArray(array ,arrayIndex, Nudgeindex, isPositive)
{//nudge index is designed to act like a variable learning rate modifier, as it tests, jumps decrease in size
let returnArray = [];
array.forEach(x=>returnArray.push(x));
let value = returnArray[arrayIndex];
if(isPositive)
{
value+=(Math.random(0,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
else
{
value+=(Math.random(-1,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
returnArray.splice(arrayIndex,1,value);
return returnArray;
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>
In javascript, i have a loop running and afterwards the pairs array which should be populated is empty.
function pair(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function problem() {
let y = 0;
let o = 0;
let pairs = [];
let c = 0;
//id, id1, history1, history2 are arrays which are populated from the json files using fetch.
while (o < id.length) {
y = 0;
while (y < id1.length) {
if (history1[y] != 0) {
if ((history2[y] != id[o]) && (history2[history1[y]] != id[o])) {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
} else {
pairs[c] = new pair(id1[y], id[o]);
c++;
}
y++;
}
o++;
}
console.log(pairs);
console.log(pairs.length);
}
When i run a debugger the array is populated and everything is fine, but when i execute the function on button click or through a console, it just returns an empty array. What could be causing this problem?
EDIT: I accidentally pasted the pair function inside the problem function, which isn't the case. I have moved it out now. And changed leght to length as suggested.
EDIT2: Here is the full code, sorry about the variable names, they are in Serbian.
The loop that processes the arrays is outside the fetch callback functions, so it doesn't wait for the arrays to be populated. See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference.
You can make your function async and use await to wait for them.
Note also that the code that calls this will need to use await or .then() to get the returned parovi array. See How do I return the response from an asynchronous call?
async function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
await fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
});
await fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
}
});
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
<html>
<head>
<title>Sheepify</title>
<script src="main.js"></script>
</head>
<body>
<button onclick="breeding()"> Breeding </button>
</body>
</html>
how are you using this problem function within the fetch?
If you are using int outside the fetch its totaly normal, your arrays will be empty.
Edit:
As you have post the fetch method, you need to put all your while loop un the last then, when you are populating the data, because fetch is async =/
Since you have two fetch, and both are populating the array's you need to do the second in the last then of the first fetch. And finaly you can do your while loop in last then of your second fetch.
It may be more clearner using async await method.
function par(idF, idM) {
this.IDOvna = idM;
this.IDOvce = idF;
}
function breeding() {
let idOvce = [];
let brOvce = [];
let mesecOvce = [];
let godinaOvce = [];
let istorija1 = [];
let istorija2 = [];
let idOvna = [];
let brOvna = [];
let mesecOvna = [];
let godinaOvna = [];
let y = 0;
let o = 0;
let parovi = [];
let c = 0;
fetch("http://localhost/ovce/ovce.json")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
for (let i = 0; i < data.ovce.length; i++) {
idOvce[i] = data.ovce[i].id;
brOvce[i] = data.ovce[i].broj;
mesecOvce[i] = data.ovce[i].mesec;
godinaOvce[i] = data.ovce[i].godina;
istorija1[i] = data.ovce[i].istorija1;
istorija2[i] = data.ovce[i].istorija2;
}
fetch("http://localhost/ovce/ovnovi.json")
.then(function(resp1) {
return resp1.json();
})
.then(function(data1) {
console.log(data1);
for (let g = 0; g < data1.ovnovi.length; g++) {
idOvna[g] = data1.ovnovi[g].id;
brOvna[g] = data1.ovnovi[g].broj;
mesecOvna[g] = data1.ovnovi[g].mesec;
godinaOvna[g] = data1.ovnovi[g].godina;
while (o < idOvna.length) {
y = 0;
while (y < idOvce.length) {
if (istorija1[y] != 0) {
if ((istorija2[y] != idOvna[o]) && (istorija2[istorija1[y]] != idOvna[o])) {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
} else {
parovi[c] = new par(idOvce[y], idOvna[o]);
c++;
}
y++;
}
o++;
}
console.log(parovi);
return parovi;
}
});
});
}
I am able to display the sum value of 6 DOM elements with a "line_cost" tag name- this is displayed as a sub-total(seen as "ST" methods). Unfortunately, while dynamically calculating the delivery-cost using the sub-total values, I consistently get "undefined" as a value being displayed under the delivery-cost tab while sub-total works perfectly.
I thought to believe the problem was occurring due to the DOM not fully loading before the method was called, yet even with the decision if(document.getElementById("sub_total") != null), I still achieve an 'undefined' value. What could be the problem for this 'undefined' value occurring?
function calcST()
{
var i;
var sum = 0; // initialize the sum
let p = document.getElementsByTagName("line_cost");
for (i = 0; i < p.length; i++)
{
if (!isNaN(Number(p[i].innerHTML)))
{
sum = Number(sum + Number(p[i].innerHTML)); // p[i].innerHTML gives you the value
}
}
setST(sum, "sub_total");
}
function setST(sum, item_id)
{
let i = document.getElementById(item_id);
i.innerHTML = sum;
calcDelivCharge();
}
function getST()
{
if (document.getElementById("sub_total") != null)
{
let i = document.getElementById("sub_total");
let v = i.innerHTML;
return v;
}
}
function calcDelivCharge()
{
var delCharge;
var e = getST();
if (e < 100)
{
delcharge = e*0.10
}
else
{
delcharge = 0;
}
setDelivCharge("delivery_charge", delCharge);
}
function setDelivCharge(item_id, delCharge)
{
let i = document.getElementById(item_id);
i.innerHTML = delCharge;
calculateTAX();
}
function getDelivCharge()
{
let i = document.getElementById("delivery_charge");
let v = i.innerHTML;
return v;
}
I'm working on a simon game and is doing a sequence of 3 at level 2 instead of doing just 2 at level 2. I've looked all over. and I've trying output to console, but I guess I've been staring at this for too long. If someone can find the bug, please share. thanks for the help.
here's the pen
https://codepen.io/zentech/pen/XaYygR
//variables
userSeq = [];
simonSeq = [];
const NUM_OF_LEVELS = 5;
var id, color, level = 0;
var strict = false;
var error = false;
var boardSound = [
"http://www.soundjay.com/button/sounds/button-4.mp3", //green
"http://www.soundjay.com/button/sounds/button-09.mp3", //red
"http://www.soundjay.com/button/sounds/button-10.mp3", //yellow
"http://www.soundjay.com/button/sounds/button-7.mp3" //blue
];
//1- start board sequence
$(document).ready(function() {
$(".start").click(function() {
strict = false;
error = false;
level++;
simonSeq = userSeq = [];
simonSequence();
})
//user pad listener
$(".pad").click(function() {
id = $(this).attr("id");
color = $(this).attr("class").split(" ")[1];
userSequence();
});
//strict mode listener
$(".strict").click(function() {
level = 0;
level++;
simonSeq = userSeq = [];
strict = true;
simonSequence();
})
})
//user sequence
function userSequence() {
userSeq.push(id);
console.log(id+" "+color);
addClassSound(id, color);
//check user sequence
if(!checkUserSeq()) {
//if playing strict mode reset everything lol
if(strict) {
console.log("strict");
simonSeq = [];
level = 1;
}
displayError();
userSeq = [];
error = true;
console.log("start simon error")
simonSequence();
}
//checking end of sequence
else if(userSeq.length == simonSeq.length && userSeq.length < NUM_OF_LEVELS) {
level++;
userSeq = [];
error = false;
console.log("start simon")
simonSequence();
}
//checking for winners
if(userSeq.length == NUM_OF_LEVELS) {
displayWinner();
resetGame();
}
}
/* simon sequence */
function simonSequence() {
console.log("level "+level);
$(".display").text(level);
if(!error) {
getRandomNum();
}
var i = 0;
var myInterval = setInterval(function() {
id = simonSeq[i];
color = $("#"+id).attr("class");
color = color.split(" ")[1];
console.log(id+" "+color);
addClassSound(id, color);
i++;
if(i == simonSeq.length) {
clearInterval(myInterval);
}
}, 1000);
}
//generate random number
function getRandomNum() {
var random = Math.floor(Math.random() * 4);
simonSeq.push(random);
}
/* add temporary class and sound */
function addClassSound(id, color) {
$("#"+id).addClass(color+"-active");
playSound(id)
setTimeout(function(){
$("#"+id).removeClass(color+"-active");
}, 500);
}
/* checking user seq against simon's */
function checkUserSeq() {
for(var i = 0; i < userSeq.length; i++) {
if(userSeq[i] != simonSeq[i]) {
return false;
}
}
return true;
}
/* display error */
function displayError() {
console.log("error");
var counter = 0;
var myError = setInterval(function() {
$(".display").text("Err");
counter++;
if(counter == 3) {
$(".display").text(level);
clearInterval(myError);
userSeq = [];
counter = 0;
}
}, 500);
}
//display winner
function displayWinner() {
var count = 0;
var winInterval = setInterval(function() {
count++;
$(".display").text("Win");
if(count == 5) {
clearInterval(winInterval);
$(".display").text("00");
count = 0;
}
}, 500);
}
/* play board sound */
function playSound(id) {
var sound = new Audio(boardSound[id]);
sound.play();
}
/* reset game */
function resetGame() {
userSeq = [];
simonSeq = [];
level = 0;
strict = false;
$(".display").text("00");
}
PROBLEM
You have a reference vs copy problem in your initialization code.
$(document).ready(function() {
$(".start").click(function() {
strict = false;
error = false;
level++;
simonSeq = userSeq = []; //PROBLEM !!!!
simonSequence();
})
Arrays are passed by reference, not value.
simonSeq = userSeq = [];
/* Any changes to 'userSeq' will affect 'simonSeq'.
'simonSeq' is referencing 'userSeq' */
SOLUTION
Change all instances of
simonSeq = userSeq = [];
To
simonSeq = [];
userSeq = [];
EXPLINATION
Values in JavaScript can be referred to in 2 ways; by reference and by value.
When you refer to something by value, you are copying it.
var numA = 5;
var numB = numA; //COPY numA over to numB
numA = 12; // Changes to numA will not affect numB because it was copied
console.log(numA); // 12
console.log(numB); // 5
When you refer to something by reference, your are referring/referencing it, not copying it. Any changes made to the original will affect everything that is referencing it.
var original = [1,2,3];
var ref = original; //Any changes made to 'original' will affect 'ref'
original.push('APPLES');
console.log(original); // [1,2,3,'APPLES']
console.log(ref); // [1,2,3,'APPLES']
In the above code ref does not actually contain any values. ref contains the memory location of original.
ref is referencing original.
Arrays and Objects are always passed/refereed to by reference.
Everything else is passed/refereed to by value (they are copied).
I wrote my code to search string for keywords and extracting needed data, but I have problems when I'm trying to search with keywords in arrays named: sort, title and artist. When I'm doing it I get an error about potential infinite loop.
var type = ['track','tracks','song','songs','album','albums'];
var artist = ['created by', 'made by'];
var genre = ['genre'];
var limit = ['set limit'];
var title = ['title','name'];
var sort = ['sort by', 'classificate by', 'separate by'];
var sortBy = ['popularity'];
// function changeWordsToNumbers(words) {
// if (msg.numbers[words])
// return msg.numbers[words];
// }
function searchForIndex(instruction, keywords) {
for (i = 0; i < keywords.length; i++) {
let search = instruction.search(keywords[i]);
if (search)
return search;
}
return false;
}
function getSearchResult(wanted) {
var searchResult = {
artist : searchForIndex(wanted, artist),
genre : searchForIndex(wanted, genre),
limit : searchForIndex(wanted, limit),
type : searchForIndex(wanted, type),
title : searchForIndex(wanted, title),
sort : searchForIndex(wanted, sort),
sortBy : searchForIndex(wanted, sortBy)
};
return searchResult;
}
function removeJunkKeyword(searchResult,instruction) {
for(var property in searchResult) {
if(searchResult.hasOwnProperty(property)) {
if(searchResult[property] != - 1) {
instruction = instruction.slice(0, searchResult[property] - 1);
}
}
}
return instruction;
}
function checkExist(searchResult) {
for(var property in searchResult) {
if(searchResult.hasOwnProperty(property)) {
if(searchResult[property] != -1)
return false;
}
}
return true;
}
function findAndCleanQuery(instruction, keywords) {
var exist = instruction.search(keywords);
var result = instruction.slice(exist + keywords.length + 1, instruction.length);
var searchResult = getSearchResult(result);
if (exist != -1) {
if (checkExist(searchResult)) {
return result;
} else {
result = removeJunkKeyword(searchResult,result);
return result;
}
}
return false;
}
function searchFor(instruction, keywords) {
for (i = 0; i < keywords.length; i++) {
let result = findAndCleanQuery(instruction,keywords[i]);
if (result)
return result;
}
return false;
}
function searchForType(instruction) {
for (i = 0; i < type.length; i++) {
let search = instruction.search(type[i])
if(search)
return type[i];
}
return false;
}
function searchForKeywords(instruction) {
msg.artist = searchFor(instruction, artist);
msg.type = searchForType(instruction, type);
msg.genre = searchFor(instruction, genre);
msg.limit = searchFor(instruction, limit);
msg.title = searchFor(instruction, title);
msg.sort = searchFor(instruction, sortreg);
}
var msg = {}
msg.instruction = 'Search for me';
searchForKeywords(msg.instruction);
console.log(msg.artist);
console.log(msg.type);
console.log(msg.genre);
console.log(msg.limit);
console.log(msg.title);
console.log(msg.sort);
Link for code: https://repl.it/J4Mc/9
PS. The object msg is used by node-red to communicate between nodes.
The issue is that you're doing this on several of your loops:
for (i = 0; i < keywords.length; i++) {
...where you should be doing this instead:
for (let i = 0; i < keywords.length; i++) {
Without using let, the variable i is effectively global. Each time you went into a new loop it got reset to 0, so it was never able to increase, which created the infinite loop.
As a side note, you'll also notice sortreg is undefined when it's used on line 98.
I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}