diff --git a/dist/examples/examples.js b/dist/examples/examples.js index 62c077e..2a251da 100644 --- a/dist/examples/examples.js +++ b/dist/examples/examples.js @@ -1519,13 +1519,32 @@ PRINT ' DEF FNnumStr$(x) = RIGHT$(STR$(x), LEN(STR$(x)) - 1) ' +PRINT "Properties of "; FNnumStr$(n); "!:" +GOSUB 400 ' number of digits of n! GOSUB 500 ' trailing zeroes of n! (for checking the result) PRINT +FRAME GOSUB 1000 ' algorithm1 PRINT +FRAME GOSUB 2000 ' algorithm2 END ' +REM Calculate number of digits +REM https://www.geeksforgeeks.org/count-digits-in-a-factorial-using-logarithm/ +400 digits = 0 +FOR i = 2 TO n + digits = digits + LOG10(i) +NEXT +digits = INT(digits) + 1 +'or use Stirling's formula: +econst = 2.718281828459045 +digits2 = n * LOG10(n / econst) + LOG10(2 * PI * n) / 2.0 +digits2 = INT(digits2+1) +if digits<>digits2 THEN ?"digits calculation differs:"; digits; digits2 +PRINT "Number of digits:"; digits +RETURN +' REM Calculate number of trailing zeroes (to check the result) REM https://www.geeksforgeeks.org/count-trailing-zeroes-factorial-number/ 500 trailz = 0 @@ -1534,18 +1553,13 @@ WHILE INT(n / i) >= 1 trailz = trailz + INT(n / i) i = i * 5 WEND -PRINT "Number of trailing zeroes for "; FNnumStr$(n); "!:"; trailz +PRINT "Number of trailing zeroes:"; trailz RETURN ' REM factorial 1 1000 t = TIME -REM Calculate number of 5-digit blocks -l = 1 -FOR i = 2 TO n - l = l + LOG10(i) -NEXT -ri = INT(l / 5 + 1) -DIM r(ri) +ri = INT(digits / 5) - (digits mod 5 <> 0): 'number of 5-digit blocks +DIM r(ri + 1): 'one more to allow carry r(1) = 1 ' REM Calculate factorial using sum of logarithms @@ -1592,20 +1606,15 @@ RETURN REM factorial2 REM based on: https://www.geeksforgeeks.org/factorial-large-number/ 2000 t = TIME -l = 1 -FOR i = 2 TO n - l = l + LOG10(i) -NEXT -ri2 = INT(l) + 1 -DIM res(ri2) +DIM res(digits) res(1) = 1 resSize = 1 FOR x = 2 TO n carry = 0 FOR i = 1 TO resSize prod = res(i) * x + carry - res(i) = prod MOD 10 carry = INT(prod / 10) + res(i) = prod - carry * 10 NEXT i WHILE carry > 0 resSize = resSize + 1 @@ -2443,6 +2452,63 @@ END ' `); +cpcBasic.addItem("", ` + REM sierpin - Sierpinski triangle + REM see also: https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle + DEG + MODE 2 + DIM cx(5),cy(5),r(5),lc(5),s$(80,25),smc(25) + cx(1)=320*80/640:cy(1)=140*25/400 + r(1)=75:r(2)=40:r(3)=20:r(4)=12:r(5)=8 + ' + sa=120 + st=1 + GOSUB 800: 'init + GOSUB 1000: 'compute + GOSUB 2000: 'output + END + ' + ' Initialize output array + 800 FOR r1=1 TO 25 + FOR c1=1 TO 79 + s$(c1,r1)=" " + NEXT + NEXT + RETURN + ' + ' Compute + 1000 cx1=ROUND(cx(st)) + cy1=ROUND((25-cy(st))) + 'lc(st)=0 + s$(cx1,cy1)=chr$(48+st) + IF cx1>smc(cy1) THEN smc(cy1)=cx1 'update max column + IF st<5 THEN GOSUB 2000:FRAME: 'intermediate output + IF st=5 THEN RETURN + lc(st)=0 + start=1 + WHILE (lc(st) MOD 360)<>0 OR start=1 + start=0 + cx(st+1)=cx(st)+1.7*80/640*r(st)*SIN(sa+lc(st)) + cy(st+1)=cy(st)+1.7*25/400*r(st)*COS(sa+lc(st)) + st=st+1 + GOSUB 1000 'recursive call + st=st-1 + lc(st)=lc(st)+2*sa + WEND + RETURN + ' + ' Output + 2000 CLS + FOR r1=1 TO 25 + FOR c1=1 TO smc(r1) + PRINT s$(c1,r1); + NEXT + PRINT + NEXT + RETURN + ' +`); + cpcBasic.addItem("", ` REM sievebe - Sieve Benchmark MODE 2 @@ -2540,62 +2606,6 @@ DATA -1,-1,-1: '"end" ' `); -cpcBasic.addItem("", ` -REM test1 - Test 1 -DEG -MODE 2 -DIM cx(5),cy(5),r(5),lc(5),s$(80,25),smc(25) -cx(1)=320*80/640:cy(1)=140*25/400 -r(1)=75:r(2)=40:r(3)=20:r(4)=12:r(5)=8 -' -sa=120 -st=1 -GOSUB 800: 'init -GOSUB 1000: 'compute -GOSUB 2000: 'output -END -' -' Initialize output array -800 FOR r1=1 TO 25 - FOR c1=1 TO 79 - s$(c1,r1)=" " - NEXT -NEXT -RETURN -' -' Compute -1000 cx1=ROUND(cx(st)) -cy1=ROUND((25-cy(st))) -'lc(st)=0 -s$(cx1,cy1)=chr$(48+st) -IF cx1>smc(cy1) THEN smc(cy1)=cx1 'update max column -IF st<5 THEN GOSUB 2000:FRAME: 'intermediate output -IF st=5 THEN RETURN -lc(st)=0 -start=1 -WHILE (lc(st) MOD 360)<>0 OR start=1 - start=0 - cx(st+1)=cx(st)+1.7*80/640*r(st)*SIN(sa+lc(st)) - cy(st+1)=cy(st)+1.7*25/400*r(st)*COS(sa+lc(st)) - st=st+1 - GOSUB 1000 'recursive call - st=st-1 - lc(st)=lc(st)+2*sa -WEND -RETURN -' -' Output -2000 CLS -FOR r1=1 TO 25 - FOR c1=1 TO smc(r1) - PRINT s$(c1,r1); - NEXT - PRINT -NEXT -RETURN -' -`); - cpcBasic.addItem("", ` 10 REM testinpu - Test Input 20 CLS diff --git a/dist/index.html b/dist/index.html index 9752967..4662adf 100644 --- a/dist/index.html +++ b/dist/index.html @@ -4,7 +4,7 @@ - LocoBasic v0.1.35 + LocoBasic v0.1.36 diff --git a/package.json b/package.json index 8225871..87b71c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "locobasic", - "version": "0.1.35", + "version": "0.1.36", "description": "# LocoBasic - Loco BASIC", "type": "commonjs", "scripts": { diff --git a/rollup.config.mjs b/rollup.config.mjs index d72576d..c24f69b 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -6,7 +6,7 @@ export default [ input: 'src/main.ts', // main entry output: { file: 'dist/locobasic.js', - format: 'umd', // "es" ECMAScript-Module + format: 'umd', sourcemap: true, name: 'locobasic', globals: { @@ -19,11 +19,15 @@ export default [ input: "./src/UI/UI.ts", // main entry output: { file: "dist/locobasicUI.js", - format: "umd", // "es" ECMAScript-Module + format: "umd", sourcemap: true, name: "locobasicUI" }, - plugins: [typescript()] + plugins: [ + typescript({ + tsconfig: "./src/UI/tsconfig.json" + }) + ] } ]; diff --git a/src/BasicVmBrowser.ts b/src/BasicVmBrowser.ts index 8f9e842..e4a7bcd 100644 --- a/src/BasicVmBrowser.ts +++ b/src/BasicVmBrowser.ts @@ -32,11 +32,11 @@ export class BasicVmBrowser extends BasicVmCore { /** * Prompts the user with a message and returns the input. * @param msg - The message to prompt. - * @returns The user input. + * @returns A promise that resolves to the user input or null if canceled. */ - public fnOnPrompt(msg: string): string | null { + public async fnOnPrompt(msg: string): Promise { const input = this.ui.prompt(msg); - return input; + return Promise.resolve(input); } /** diff --git a/src/BasicVmCore.ts b/src/BasicVmCore.ts index fb0ff3c..9ab67c6 100644 --- a/src/BasicVmCore.ts +++ b/src/BasicVmCore.ts @@ -26,7 +26,7 @@ export class BasicVmCore implements IVmAdmin { // override } - protected fnOnPrompt(_msg: string): string | null { // eslint-disable-line @typescript-eslint/no-unused-vars + protected async fnOnPrompt(_msg: string): Promise { // eslint-disable-line @typescript-eslint/no-unused-vars // override return ""; } @@ -133,7 +133,7 @@ export class BasicVmCore implements IVmAdmin { this.output += args.join(''); } - public prompt(msg: string): string | null { + public prompt(msg: string): Promise { this.flush(); return this.fnOnPrompt(msg); }; diff --git a/src/BasicVmNode.ts b/src/BasicVmNode.ts index 6553c3f..fc8f1ed 100644 --- a/src/BasicVmNode.ts +++ b/src/BasicVmNode.ts @@ -62,9 +62,9 @@ export class BasicVmNode extends BasicVmCore { console.log(msg.replace(/\n$/, "")); } - public fnOnPrompt(msg: string): string { + public async fnOnPrompt(msg: string): Promise { console.log(msg); - return ""; + return Promise.resolve(""); } public fnGetPenColor(num: number): string { diff --git a/src/Interfaces.ts b/src/Interfaces.ts index bc2cc42..0054c9c 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -21,7 +21,7 @@ export interface IVm { paper(color: number): void; pen(color: number): void; print(_msg: string): void; - prompt(_msg: string): string | null; + prompt(_msg: string): Promise; } export interface IVmAdmin extends IVm { diff --git a/src/NodeParts.ts b/src/NodeParts.ts index 7d8873d..1cafdef 100644 --- a/src/NodeParts.ts +++ b/src/NodeParts.ts @@ -29,7 +29,7 @@ const dummyVm: DummyVm = { paper(num: number) { this.debug("paper:", num); }, pen(num: number) { this.debug("pen:", num); }, print(...args: (string | number)[]) { this._output += args.join(''); }, - prompt(msg: string) { console.log(msg); return ""; } + async prompt(msg: string) { console.log(msg); return ""; } }; export class NodeParts { diff --git a/src/Semantics.ts b/src/Semantics.ts index 09e10cf..6cd3aa7 100644 --- a/src/Semantics.ts +++ b/src/Semantics.ts @@ -8,6 +8,7 @@ function getCodeSnippets() { const _data: (string | number)[] = []; let _dataPtr = 0; const _restoreMap: Record = {}; + const frame = async () => {}; // dummy const codeSnippets = { bin$: function bin$(num: number, pad: number = 0): string { @@ -42,7 +43,7 @@ function getCodeSnippets() { _o.flush(); return "end"; }, - frame: function frame() { // async + frame: async function frame() { _o.flush(); return new Promise(resolve => setTimeout(() => resolve(), Date.now() % 50)); }, @@ -52,13 +53,17 @@ function getCodeSnippets() { hex$: function hex$(num: number, pad?: number) { return num.toString(16).toUpperCase().padStart(pad || 0, "0"); }, - input: function input(msg: string, isNum: boolean) { // async - _o.flush(); - return new Promise(resolve => setTimeout(() => { - const input = _o.prompt(msg); - resolve(isNum ? Number(input) : input); - }, 5)); - }, + input: async function input(msg: string, isNum: boolean) { + await frame(); + const input = await _o.prompt(msg); + if (input === null) { + throw new Error("Input canceled"); + } else if (isNum && isNaN(Number(input))) { + throw new Error("Invalid number input"); + } else { + return isNum ? Number(input) : input; + } + }, mid$Assign: function mid$Assign(s: string, start: number, newString: string, len?: number) { start -= 1; len = Math.min(len ?? newString.length, newString.length, s.length - start); @@ -225,11 +230,9 @@ function getSemantics(semanticsHelper: ISemanticsHelper): ActionDict { if (instrMap[key]) { const code = String((codeSnippets[key as keyof typeof codeSnippets]).toString()); const adaptedCode = trimIndent(code); - if (adaptedCode.includes("Promise") || adaptedCode.includes("await")) { - lineList.push("async " + adaptedCode); + lineList.push(adaptedCode); + if (adaptedCode.startsWith("async ")) { needsAsync = true; - } else { - lineList.push(adaptedCode); } } } @@ -507,6 +510,7 @@ function getSemantics(semanticsHelper: ISemanticsHelper): ActionDict { Input(_inputLit: Node, message: Node, _semi: Node, e: Node) { semanticsHelper.addInstr("input"); + semanticsHelper.addInstr("frame"); const messageString = message.sourceString.replace(/\s*[;,]$/, ""); const identifier = e.eval(); diff --git a/src/UI/tsconfig.json b/src/UI/tsconfig.json index cfb754b..29f2537 100644 --- a/src/UI/tsconfig.json +++ b/src/UI/tsconfig.json @@ -3,11 +3,11 @@ "compilerOptions": { "outDir": "../../dist/UI", "declarationDir": "../../dist/UI/types", - //"sourceRoot":"./src/UI", "lib": [ "es2020", "dom" ], + "rootDir": "../", "composite": true, }, "include": ["./*.ts"], diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json index 9547cd3..4ff078d 100644 --- a/src/tsconfig-base.json +++ b/src/tsconfig-base.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "ESNext", - "target": "ES6", + "target": "ES2017", "moduleResolution": "node", "esModuleInterop": true, "strict": true, diff --git a/tsconfig.json b/tsconfig.json index 7646a42..35cf2a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,5 @@ { - "compilerOptions": { - "module": "ESNext", - "target": "ES6", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - }, - + "extends": "./src/tsconfig-base.json", "files": [], "include": [], "references": [