How to make a fully functional brainf*ck interpreter? - javascript

I have tried to implement a BF interpreter in Javascript. It works for many programs like printing Hello world, looping, etc.
Here is link to a sample interpreter that I use for comparing outputs: https://sange.fi/esoteric/brainfuck/impl/interp/i.html
But when I try to run a BF to C program, it gets stuck like it is in an infinite loop. It however does work in the sample interpreter above. What am I doing wrong?
Here is a BF code that converts an input BF code to C.
+++[>+++++<-]>>+<[>>++++>++>+++++>+++++>+>>+<++[++<]>---]
>++++.>>>.+++++.>------.<--.+++++++++.>+.+.<<<<---.[>]<<.<<<.-------.>++++.
<+++++.+.>-----.>+.<++++.>>++.>-----.
<<<-----.+++++.-------.<--.<<<.>>>.<<+.>------.-..--.+++.-----<++.<--[>+<-]
>>>>>--.--.<++++.>>-.<<<.>>>--.>.
<<<<-----.>----.++++++++.----<+.+++++++++>>--.+.++<<<<.[>]<.>>
,[>>+++[<+++++++>-]<[<[-[-<]]>>[>]<-]<[<+++++>-[<+++>-[<-->-[<+++>-
[<++++[>[->>]<[>>]<<-]>[<+++>-[<--->-[<++++>-[<+++[>[-[-[-[->>]]]]<[>>]<<-]
>[<+>-[<->-[<++>-[<[-]>-]]]]]]]]]]]]]
<[
-[-[>+<-]>]
<[<<<<.>+++.+.+++.-------.>---.++.<.>-.++<<<<.[>]>>>>>>>>>]
<[[<]>++.--[>]>>>>>>>>]
<[<<++..-->>>>>>]
<[<<..>>>>>]
<[<<..-.+>>>>]
<[<<++..---.+>>>]
<[<<<.>>.>>>>>]
<[<<<<-----.+++++>.----.+++.+>---.<<<-.[>]>]
<[<<<<.-----.>++++.<++.+++>----.>---.<<<.-[>]]
<[<<<<<----.>>.<<.+++++.>>>+.++>.>>]
<.>
]>
,]
<<<<<.<+.>++++.<----.>>---.<<<-.>>>+.>.>.[<]>++.[>]<.
Here is my implementation:
class Node {
constructor() {
this.value = 0;
this.next = null;
this.prev = null;
}
increment() {
this.value++;
}
decrement() {
this.value--;
}
}
class Memory {
constructor() {
this.current = new Node();
this.outputBuffer = [];
}
moveRight() {
if (this.current.next === null) {
const rightNode = new Node();
rightNode.prev = this.current
this.current.next = rightNode;
}
this.current = this.current.next;
}
moveLeft() {
if (this.current.prev === null) {
const leftNode = new Node()
leftNode.next = this.current;
this.current.prev = leftNode;
}
this.current = this.current.prev;
}
increment() {
this.current.increment();
}
decrement() {
this.current.decrement();
}
print() {
this.outputBuffer.push(String.fromCharCode(this.current.value));
}
input(ch) {
this.current.value = ch.charCodeAt(0);
}
}
class Interpreter {
reset() {
this.memory = new Memory();
this.instructionPointer = 0;
this.inputPointer = 0;
this.openingToClosingBrackets = new Map();
this.closingToOpeningBrackets = new Map();
}
interpret(code, input = "") {
this.reset();
this.code = code;
this.matchSquareBrackets();
this.input = input;
while (!this.reachedEOF()) {
const instruction = this.code[this.instructionPointer];
switch (instruction) {
case "+": this.memory.increment(); break;
case "-": this.memory.decrement(); break;
case ">": this.memory.moveRight(); break;
case "<": this.memory.moveLeft(); break;
case ".": this.memory.print(); break;
case ",": this.memory.input(this.getNextCharacter()); break;
case "[": this.loopStart(); break;
case "]": this.loopEnd(); break;
}
this.instructionPointer++;
}
return this.memory.outputBuffer.join("");
}
reachedEOF() {
return this.instructionPointer >= this.code.length;
}
getNextCharacter() {
if (this.inputPointer >= this.input.length) {
throw new Error("EOF. Expected more input characters.");
}
return this.input[this.inputPointer];
}
loopStart() {
if (this.memory.current.value !== 0) {
return;
}
this.instructionPointer = this.openingToClosingBrackets.get(
this.instructionPointer
);
}
loopEnd() {
if (this.memory.current.value === 0) {
return;
}
this.instructionPointer = this.closingToOpeningBrackets.get(
this.instructionPointer
);
}
matchSquareBrackets() {
const openingStack = [];
for (let i = 0; i < this.code.length; i++) {
const ch = this.code[i];
if (ch === "[") {
openingStack.push(i);
}
if (ch === "]") {
if (openingStack.length === 0) {
throw new Error("No matching '[' for ']' at index: " + i);
}
const openingMatch = openingStack.pop();
this.openingToClosingBrackets.set(openingMatch, i);
this.closingToOpeningBrackets.set(i, openingMatch);
}
}
if (openingStack.length > 0) {
throw new Error(
"No matching ']' for '[' at indices: " + openingStack.join(", ")
);
}
}
}

Your getNextCharacter doesn't work correctly: if there's at least one character of input, it will return that character each time it's called - it never increments the input index. Since the bf2c program keeps reading input until there is no more input, this causes your infinite loop.
Another problem with your code is that you throw an exception when , is used and there is no more input, causing the bf2c to abort with an exception when it reaches the end of the input. So you'll either need to explicitly terminate the input with a \0, so that the bf2c program knows when to stop reading or change getNextCharacter to return '\0' at the end of input instead of throwing an exception.

Related

Why is my decryption function not working?

I created a function to encrypt and decrypt messages. the encrypting works fine. but when I try to log encrypted Hello World! it just logs H.
const chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".split("");
const numbs = "0123456789".split("");
const symbols = "!##$%^&*()_+-=[]{}|;':,./<>?\" ".split("");
function encrypt(message) {
message = message.split("")
var output = []
message.forEach(element => {
if (chars.includes(element)) {
output.push("c" + chars.indexOf(element))
} else if (numbs.includes(element)) {
output.push("n" + numbs.indexOf(element))
} else if (symbols.includes(element)) {
output.push("s" + symbols.indexOf(element))
} else {
console.log(element)
throw new Error(`Unknown character`)
}
});
return output.join("")
}
function decrypt(message) {
message = message.split("");
var output = [];
var prevDeter;
var prevNumbs = [];
message.forEach(element => {
if (element == "c") {
prevDeter = "c"
if (prevNumbs.length > 0) {
output.push(chars[parseInt(prevNumbs.join(""))])
}
} else if (element == "n") {
prevDeter = "n"
if (prevNumbs.length > 0) {
output.push(numbs[parseInt(prevNumbs.join(""))])
}
} else if (element == "s") {
prevDeter = "s"
if (prevNumbs.length > 0) {
output.push(symbols[parseInt(prevNumbs.join(""))])
}
} else {
prevNumbs.push(element)
}
});
return output.join("")
}
//expected to log Hello World! but logs H and when starting the message with a symbol or number it just logs nothing
console.log(decrypt(encrypt("Hello World!")))
Fixed it, i edited the encoding system to place a - between chars and the decoding system to just split the message at - and check if the element starts with c n or s. and then i just used substring to get the number and decrypt it
const chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".split("");
const numbs = "0123456789".split("");
const symbols = "!##$%^&*()_+-=[]{}|;':,./<>?\" ".split("");
function encrypt(message) {
message = message.split("");
var output = [];
message.forEach(element => {
if(chars.includes(element)) {
output.push("-c" + chars.indexOf(element));
}else if(numbs.includes(element)) {
output.push("-n" + numbs.indexOf(element));
}else if(symbols.includes(element)) {
output.push("-s" + symbols.indexOf(element));
}else{
console.log(element);
throw new Error(`Unknown character`);
};
});
return output.join("");
};
function decrypt(message) {
message = message.split("-");
console.log(message)
var output = [];
message.forEach(element => {
if(element.startsWith("c")) {
output.push(chars[element.substring(1)]);
}else if(element.startsWith("n")) {
output.push(numbs[element.substring(1)]);
}else if(element.startsWith("s")) {
output.push(symbols[element.substring(1)]);
}else if(element.length < 1){
}else{
throw new Error(`Invalid message`);
}
});
return output.join("");
};
console.log(decrypt(encrypt("Hello World!")));
You need to split the encoded string based on set/index pairs. This is easy enough to do with a look-ahead regular expression and splitting before a c, n or an s. /(?=[cns])/
const chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".split("");
const numbs = "0123456789".split("");
const symbols = "!##$%^&*()_+-=[]{}|;':,./<>?\" ".split("");
function encrypt(message) {
message = message.split("")
var output = []
message.forEach(element => {
if (chars.includes(element)) {
output.push("c" + chars.indexOf(element))
} else if (numbs.includes(element)) {
output.push("n" + numbs.indexOf(element))
} else if (symbols.includes(element)) {
output.push("s" + symbols.indexOf(element))
} else {
console.log(element)
throw new Error(`Unknown character`)
}
});
return output.join("")
}
function decrypt(message) {
message = message.split(/(?=[cns])/);
var output = [];
message.forEach(element => {
let set;
switch(element[0]){
case 'c':
set = chars;
break;
case 'n':
set = numbs;
break;
case 's':
set = symbols;
break;
}
const index = parseInt(element.substring(1));
output.push(set[index]);
});
return output.join('');
}
const encrypted = encrypt("Hello World!");
console.log(encrypted);
//expected to log Hello World! but logs H and when starting the message with a symbol or number it just logs nothing
console.log(decrypt(encrypted));

Programming language interpreter in a non-english language;

I was trying to implement this using a different language. but it doesn't seem to handle the character that has been inserted, This actually works with english I tweaked it in amharic, and I was thinking to add more things if the handling works like creating an editor or something. If anyone is willing to help, I would appreciate it. I know that I should normalize the unicode characters, but I don't know efficient way I could do that.
function InputStream(input) {
var pos = 0, line = 1, col = 0;
return {
next : next,
peek : peek,
eof : eof,
croak : croak,
};
function next() {
var ch = input.charAt(pos++);
if (ch == "\n") line++, col = 0; else col++;
return ch;
}
function peek() {
return input.charAt(pos);
}
function eof() {
return peek() == "";
}
function croak(msg) {
throw new Error(msg + " (" + line + ":" + col + ")");
}
}
function TokenStream(input) {
var current = null;
var keywords = " ከ ወደ ሌላ ስራ ስራ አውነት ሀሰት ";
return {
next : next,
peek : peek,
eof : eof,
croak : input.croak
};
function is_keyword(x) {
return keywords.indexOf(" " + x + " ") >= 0;
}
function is_digit(ch) {
return /[0-9]/i.test(ch);
}
function is_id_start(ch) {
return /[^\u1380-\u1380f]+[a-z_]/i.test(ch);
}
function is_id(ch) {
return is_id_start(ch) || "?!-<>=0123456789".indexOf(ch) >= 0;
}
function is_op_char(ch) {
return "+-*/%=&|<>!".indexOf(ch) >= 0;
}
function is_punc(ch) {
return "፣፤(){}[]".indexOf(ch) >= 0;
}
function is_whitespace(ch) {
return " \t\n".indexOf(ch) >= 0;
}
function read_while(predicate) {
var str = "";
while (!input.eof() && predicate(input.peek()))
str += input.next();
return str;
}
function read_number() {
var has_dot = false;
var number = read_while(function(ch){
if (ch == ".") {
if (has_dot) return false;
has_dot = true;
return true;
}
return is_digit(ch);
});
return { type: "num", value: parseFloat(number) };
}
function read_ident() {
var id = read_while(is_id);
return {
type : is_keyword(id) ? "kw" : "var",
value : id
};
}
function read_escaped(end) {
var escaped = false, str = "";
input.next();
while (!input.eof()) {
var ch = input.next();
if (escaped) {
str += ch;
escaped = false;
} else if (ch == "\\") {
escaped = true;
} else if (ch == end) {
break;
} else {
str += ch;
}
}
return str;
}
function read_string() {
return { type: "str", value: read_escaped('"') };
}
function skip_comment() {
read_while(function(ch){ return ch != "\n" });
input.next();
}
function read_next() {
read_while(is_whitespace);
if (input.eof()) return null;
var ch = input.peek();
if (ch == "#") {
skip_comment();
return read_next();
}
if (ch == '"') return read_string();
if (is_digit(ch)) return read_number();
if (is_id_start(ch)) return read_ident();
if (is_punc(ch)) return {
type : "punc",
value : input.next()
};
if (is_op_char(ch)) return {
type : "op",
value : read_while(is_op_char)
};
input.croak("Can't handle character: " + ch);
}
function peek() {
return current || (current = read_next());
}
function next() {
var tok = current;
current = null;
return tok || read_next();
}
function eof() {
return peek() == null;
}
}
function parse(input) {
var PRECEDENCE = {
"=": 1,
"||": 2,
"&&": 3,
"<": 7, ">": 7, "<=": 7, ">=": 7, "==": 7, "!=": 7,
"+": 10, "-": 10,
"*": 20, "/": 20, "%": 20,
};
var FALSE = { type: "bool", value: false };
return parse_toplevel();
function is_punc(ch) {
var tok = input.peek();
return tok && tok.type == "punc" && (!ch || tok.value == ch) && tok;
}
function is_kw(kw) {
var tok = input.peek();
return tok && tok.type == "kw" && (!kw || tok.value == kw) && tok;
}
function is_op(op) {
var tok = input.peek();
return tok && tok.type == "op" && (!op || tok.value == op) && tok;
}
function skip_punc(ch) {
if (is_punc(ch)) input.next();
else input.croak("Expecting punctuation: \"" + ch + "\"");
}
function skip_kw(kw) {
if (is_kw(kw)) input.next();
else input.croak("Expecting keyword: \"" + kw + "\"");
}
function skip_op(op) {
if (is_op(op)) input.next();
else input.croak("Expecting operator: \"" + op + "\"");
}
function unexpected() {
input.croak("Unexpected token: " + JSON.stringify(input.peek()));
}
function maybe_binary(left, my_prec) {
var tok = is_op();
if (tok) {
var his_prec = PRECEDENCE[tok.value];
if (his_prec > my_prec) {
input.next();
return maybe_binary({
type : tok.value == "=" ? "assign" : "binary",
operator : tok.value,
left : left,
right : maybe_binary(parse_atom(), his_prec)
}, my_prec);
}
}
return left;
}
function delimited(start, stop, separator, parser) {
var a = [], first = true;
skip_punc(start);
while (!input.eof()) {
if (is_punc(stop)) break;
if (first) first = false; else skip_punc(separator);
if (is_punc(stop)) break;
a.push(parser());
}
skip_punc(stop);
return a;
}
function parse_call(func) {
return {
type: "call",
func: func,
args: delimited("(", ")", "፣", parse_expression),
};
}
function parse_varname() {
var name = input.next();
if (name.type != "var") input.croak("Expecting variable name");
return name.value;
}
function parse_if() {
skip_kw("ከ");
var cond = parse_expression();
if (!is_punc("{")) skip_kw("ወደ");
var then = parse_expression();
var ret = {
type: "if",
cond: cond,
then: then,
};
if (is_kw("ሌላ")) {
input.next();
ret.else = parse_expression();
}
return ret;
}
function parse_lambda() {
return {
type: "lambda",
vars: delimited("(", ")", "፣", parse_varname),
body: parse_expression()
};
}
function parse_bool() {
return {
type : "bool",
value : input.next().value == "አውነት"
};
}
function maybe_call(expr) {
expr = expr();
return is_punc("(") ? parse_call(expr) : expr;
}
function parse_atom() {
return maybe_call(function(){
if (is_punc("(")) {
input.next();
var exp = parse_expression();
skip_punc(")");
return exp;
}
if (is_punc("{")) return parse_prog();
if (is_kw("if")) return parse_if();
if (is_kw("አውነት") || is_kw("ሀሰት")) return parse_bool();
if (is_kw("ስራ") || is_kw("ሥራ")) {
input.next();
return parse_lambda();
}
var tok = input.next();
if (tok.type == "var" || tok.type == "num" || tok.type == "str")
return tok;
unexpected();
});
}
function parse_toplevel() {
var prog = [];
while (!input.eof()) {
prog.push(parse_expression());
if (!input.eof()) skip_punc("፤");
}
return { type: "prog", prog: prog };
}
function parse_prog() {
var prog = delimited("{", "}", "፤", parse_expression);
if (prog.length == 0) return FALSE;
if (prog.length == 1) return prog[0];
return { type: "prog", prog: prog };
}
function parse_expression() {
return maybe_call(function(){
return maybe_binary(parse_atom(), 0);
});
}
}
function Environment(parent) {
this.vars = Object.create(parent ? parent.vars : null);
this.parent = parent;
}
Environment.prototype = {
extend: function() {
return new Environment(this);
},
lookup: function(name) {
var scope = this;
while (scope) {
if (Object.prototype.hasOwnProperty.call(scope.vars, name))
return scope;
scope = scope.parent;
}
},
get: function(name) {
if (name in this.vars)
return this.vars[name];
throw new Error("Undefined variable " + name);
},
set: function(name, value) {
var scope = this.lookup(name);
if (!scope && this.parent)
throw new Error("Undefined variable " + name);
return (scope || this).vars[name] = value;
},
def: function(name, value) {
return this.vars[name] = value;
}
};
function evaluate(exp, env) {
switch (exp.type) {
case "num":
case "str":
case "bool":
return exp.value;
case "var":
return env.get(exp.value);
case "assign":
if (exp.left.type != "ተጠቀመም")
throw new Error("Cannot assign to " + JSON.stringify(exp.left));
return env.set(exp.left.value, evaluate(exp.right, env));
case "binary":
return apply_op(exp.operator,
evaluate(exp.left, env),
evaluate(exp.right, env));
case "lambda":
return make_lambda(env, exp);
case "if":
var cond = evaluate(exp.cond, env);
if (cond !== false) return evaluate(exp.then, env);
return exp.else ? evaluate(exp.else, env) : false;
case "prog":
var val = false;
exp.prog.forEach(function(exp){ val = evaluate(exp, env) });
return val;
case "call":
var func = evaluate(exp.func, env);
return func.apply(null, exp.args.map(function(arg){
return evaluate(arg, env);
}));
default:
throw new Error("I don't know how to evaluate " + exp.type);
}
}
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
throw new Error("Expected number but got " + x);
return x;
}
function div(x) {
if (num(x) == 0)
throw new Error("Divide by zero");
return x;
}
switch (op) {
case "+": return num(a) + num(b);
case "-": return num(a) - num(b);
case "*": return num(a) * num(b);
case "/": return num(a) / div(b);
case "%": return num(a) % div(b);
case "&&": return a !== false && b;
case "||": return a !== false ? a : b;
case "<": return num(a) < num(b);
case ">": return num(a) > num(b);
case "<=": return num(a) <= num(b);
case ">=": return num(a) >= num(b);
case "==": return a === b;
case "!=": return a !== b;
}
throw new Error("Can't apply operator " + op);
}
function make_lambda(env, exp) {
function lambda() {
var names = exp.vars;
var scope = env.extend();
for (var i = 0; i < names.length; ++i)
scope.def(names[i], i < arguments.length ? arguments[i] : false);
return evaluate(exp.body, scope);
}
return lambda;
}
/* -----[ entry point for NodeJS ]----- */
var globalEnv = new Environment();
globalEnv.def("time", function(func){
try {
console.time("time");
return func();
} finally {
console.timeEnd("time");
}
});
if (typeof process != "undefined") (function(){
var util = require("util");
globalEnv.def("ፃፍ", function(val){
console.log(val);
});
globalEnv.def("print", function(val){
util.print(val);
});
var code = "";
process.stdin.setEncoding("utf8");
process.stdin.on("readable", function(){
var chunk = process.stdin.read();
if (chunk) code += chunk;
});
process.stdin.on("end", function(){
var ast = parse(TokenStream(InputStream(code)));
evaluate(ast, globalEnv);
});
})();
Here's the english version this should be how print works "println("hello world!");" and i would like to use "ፃፍ("ሰላም ሰላም")፤" where 'print' and ';' are converted to 'ፃፍ' and "፤
That's a lot of code, and no explanation of what it fails to do, other than "it doesn't seem to handle the character that has been inserted" which could mean just about anything. If I had an Amharic keyboard layout and knew how to type with it, I might give the code a try to see what's wrong, but I don't have one and I wouldn't know how to use it, so I didn't try.
However, I think your is_id and is_id_start function is not likely to work. You have:
function is_id_start(ch) {
return /[^\u1380-\u1380f]+[a-z_]/i.test(ch);
}
function is_id(ch) {
return is_id_start(ch) || "?!-<>=0123456789".indexOf(ch) >= 0;
}
I was a little puzzled to see !-<>= as possible identifier characters, since they are also operator characters. Presumably, the intention is that if an identifier is followed by one of those operators, there must be whitespace in between. But I don't think that's the problem you are experiencing with Amharic. That seems more likely to have to do with this slightly odd regular expression: /[^\u1380-\u1380f]+[a-z_]/i.
To start with, \u1380f is not a single unicode escape character. \u must be followed by exactly four hex digits, so the f is not part of the escape. It's just an ordinary f. That makes the range \u1380-\u1380, which consists of the single character ᎀ.
It's possible that you meant \u1380-\u138f, which would be 16 of the 453 Ethiopic letters. My knowledge of Amharic is pretty limited, and certainly not adequate to understand what differentiates those particular characters, so I can't even begin to guess whether that's reasonable.
However, you use that range in an inverted regular expression; even making that correction, what [^\u1380-\u138f] matches is any character other than a character in that range. That would include characters from all over the Unicode galaxy, including lots of other scripts, so I'm pretty certain that it is not what you intended.
Furthermore, your ID start pattern is actually /[^\u1380-\u1380f]+[a-z_]/i, which means "one or more [...] followed by a Latin alphabetic character or underscore". In other words, the pattern requires at least two characters to match, first an Amharic character (or, as written, anything other than one of those Amharic characters) and then a Latin alphabetic character.
That's clearly wrong, because you are matching that pattern against the result of input.peek(), which can only be a single character. So is_id is guaranteed to return false, which is probably related to your problem.
Personally, I think you'd be a lot better off using Unicode Property classes than trying to write down a list of valid identifier characters. (Why restrict identifiers to just English and Amharic, for example?) Ecmascript conveniently comes with the standard character sets recommended by the Unicode technical experts to be used for identifiers. You could just use them:
function is_id_start(ch) {
return /\p{XID_Start}/u.test(ch);
}
function is_id(ch) {
return /[\p{XID_Continue}?!-<>=]/u.test(ch);
}
Note that you must use the u flag to enable the use of Unicode properties.
If you really wanted to restrict that to Amharic and Latin, you could do so by also requiring that the characters also match [\p{Script=Ethiopic}\p{Script=Latin}]. (You can use a lookahead assertion to do that.)

How to fix forecast looping push

How to fix the looping when clicking the nz-switch/switch button.
I'm create a switch . thank you
here's the code:
child.component.ts
#Input() toggleUnit: boolean = false;
ngOnChanges() {
switch(this.toggleUnit) {
case true: {
this.unit = 'imperial';
this.setWeatherForecast(this.unit)
break;
}case false: {
this.unit = 'metric';
this.setWeatherForecast(this.unit)
break;
}
}
}
setWeatherForecast(unit: any) {
this.weatherService.getLocation().subscribe(data => {
this.lat = JSON.parse(data['_body']).latitude;
this.lon = JSON.parse(data['_body']).longitude;
this.weatherService
.fiveDayForecast(this.lat, this.lon, unit)
.subscribe(data => {
for (let i = 0; i < data.list.length; i = i + 8) {
const forecastWeather = new Forecast(
data.city.name,
data.list[i].weather[0].description,
data.list[i].main.temp,
data.list[i].dt_txt,
data.list[i].weather[0].icon
);
this.forecast.push(forecastWeather);
}
return this.forecast;
});
});
}
parent.component.ts
toggleUnit: boolean = false;
onSwitchChange() {
let temperature: any = [];
switch (this.toggleUnit) {
case true: {
this.toggleUnit = true;
break;
}
case false: {
temperature = document.getElementById(`temperature-${i}`) as HTMLCanvasElement;
temperature.style.height =((this.tempThermometer[i].temperature - this.config.minTemp) / (this.config.maxTemp - this.config.minTemp)) * 100 + '%';
temperature.dataset.value = this.tempThermometer[i].temperature + this.units['Celcius'];
}
this.toggleUnit = false;
break;
}
}
****parent.component.html***
<app-forecast [toggleUnit]="toggleUnit"></app-forecast>
but when I try to login it instead it updates it push new row.
I use nz-swith and others.
the output will push another new array, same when it will switch it will add new array.

Simplify function with repeating if/else parts

I'm trying to simplify this function, as there can be multiple type of data objcts and for each type there is also a male and a female version.
The number and name of the elements in the objects are always identical.
As you see, most of the code is repeating...
function calculate(type, j, value, s) {
for (var i = j; i > 4; i--) {
if (type == 'weight') {
if (s == 'f') {
if (weightFemale.hasOwnProperty(i)) {
var m = weightFemale[i][0],
l = weightFemale[i][1],
s = weightFemale[i][2];
return getcalc( m,l,s );
}
}
else {
if (weightMale.hasOwnProperty(i)) {
var m = weightMale[i][0],
l = weightMale[i][1],
s = weightMale[i][2];
return getcalc( m,l,s );
}
}
}
else if (type == 'length') {
if (s == 'f') {
if (lengthFemale.hasOwnProperty(i)) {
var m = lengthFemale[i][0],
l = lengthFemale[i][1],
s = lengthFemale[i][2],
return getcalc( m,l,s );
}
}
else {
if (lengthMale.hasOwnProperty(i)) {
var m = lengthMale[i][0],
l = lengthMale[i][1],
s = lengthMale[i][2],
return getcalc( m,l,s );
}
}
}
}
return false;
}
How can I simplify the if/else-parts for the type and the sex?
Since you are doing the same thing to each object , just make your conditionals define a single object reference and only call calculation once.
Something like:
var obj;
if (type == 'weight') {
obj = s == 'f' ? weightFemale : weightMale;
} else if (type == 'length') {
obj = s == 'f' ? lengthFemale : lengthMale;
}
if (obj.hasOwnProperty(i)) {
var m = obj[i][0],
l = obj[i][1],
s = obj[i][2];
return getcalc(m, l, s);
}
I would create a own function and create a switch
switch(type) {
case "weight":
getValues();
break;
case "length":
getValues();
break;
}
Ask yourself, what are the parts that are very similar? It looks if .hasOwnProperty() is true, you get m, l, and s from the array and then call getcalc(). Start by extracting that part into a function with the differing pieces being passed in as parameters.
Another pattern is that you're using a particular array based on certain conditions. Getting the array you want can be placed into a function.
Also, not quite related to the question, but you may want to give your variables better names. This makes the code more readable and easier to reason about.
Here's what I came up with:
function getArray(type, s){
if(type == 'weight') {
return s == 'f' ? weightFemale : weightMale;
}
else if(type == 'length') {
return s == 'm' ? lengthFemale : lengthMale;
}
}
function makeCalculation(array, i) {
if(array.hasOwnProperty(i)) {
var m = lengthMale[i][0],
l = lengthMale[i][1],
s = lengthMale[i][2],
return getcalc( m,l,s );
}
}
function calculate(type, j, value, s) {
for (var i = j; i > 4; i--) {
var array = getArray(type, s);
return makeCalculation(array, i);
}
}
Firstly you can create function like this:
function _getcalc(arr, i) {
var m = arr[i][0],
l = arr[i][1],
s = arr[i][2];
return getcalc(m, l, s);
}
Then you can shorten your function like this:
function calculate(type, j, value, s) {
for (var i = j; i > 4; i--) {
switch(type) {
case 'weight':
if (s == 'f' && weightFemale.hasOwnProperty(i)) {
return _getcalc(weightFemale, i);
} else if(weightMale.hasOwnProperty(i)) {
return _getcalc(weightMale, i);
}
break;
case 'length':
if (s == 'f' && lengthFemale.hasOwnProperty(i)) {
return _getcalc(lengthFemale, i);
} else if(lengthMale.hasOwnProperty(i)) {
return _getcalc(lengthMale, i);
}
break;
}
}
return false;
}

Correcting incorrectly encoded string (ASCII characters back to UTF-8)

Here is a sample WiFi ssid I have extracted from an Android "wifi config file" (wpa_supplicant.conf).
I'm trying to display all the ssid's in the file, most are okay as they are normal strings wrapped in quotes, for example,
network={
ssid="Linksys"
...
}
However, some entries just wanted to be different and special, for example,
network={
ssid=e299aa20e6b7a1e5ae9ae69c89e98ca2e589a920e299ab
...
}
Now, the question is, how do I convert it back to a readable string (preferably in JS)? I suspect the encoding was wrong (it displays correctly on a native device though.)
Apparently the string is in hex unencoded. By turning it back to binary following by some string manipulation, I am able to encode it back to the readable form.
function HextoUTF8(txt) {
function HexStringToBytes(str) {
if (str.length % 2) throw TypeError("Not a valid length");
return [].map.call(str, function(e) {
return ("000" + parseInt(e, 16).toString(2)).slice(-4);
}).join("").match(/.{8}/g);
}
function BytesToUTF8(bytes) {
var inExpectationMode = false,
itr = new Iterator(bytes),
byte,
availableBitsTable = {
"1": -7,
"2": -5,
"3": -4,
"4": -3
},
expectingBitsLeft = 0,
currectCharacter = "",
result = "";
while (byte = itr.next(), !byte.ended) {
byte = byte.value;
if (inExpectationMode) {
currectCharacter += byte.slice(-6);
} else {
//First in sequence
expectingBitsLeft = determineSequenceLength(byte);
currectCharacter += byte.slice(availableBitsTable[expectingBitsLeft]);
}
inExpectationMode = true;
expectingBitsLeft--;
if (!expectingBitsLeft) {
inExpectationMode = false;
result += String.fromCharCode(parseInt(currectCharacter, 2));
currectCharacter = "";
}
}
return result;
}
function determineSequenceLength(byte) {
if (byte[0] === "0") return 1;
else if (byte.slice(0, 3) === "110") return 2;
else if (byte.slice(0, 4) === "1110") return 3;
else if (byte.slice(0, 5) === "11110") return 4;
}
function Iterator(array) {
if (this === window) throw TypeError("This is a class");
if (!Array.isArray(array)) throw TypeError("An array is required");
this.i = -1;
this.ended = !array.length;
this.array = function() {
return array;
};
}
Iterator.prototype.next = function() {
if (this.ended || ++this.i == this.array().length) {
this.ended = true;
return {
ended: true
};
} else {
return {
ended: this.ended,
value: this.array()[this.i]
};
}
}
return BytesToUTF8(HexStringToBytes(txt));
}
Optimally I should be doing bit manipulation instead, but whatever, it works,
> HextoUTF8("e299aa20e6b7a1e5ae9ae69c89e98ca2e589a920e299ab");
> "♪ 淡定有錢剩 ♫"

Categories