Compare commits

...

465 Commits

Author SHA1 Message Date
0b6484e0b4 fuck
All checks were successful
tagged-release / Tagged Release (push) Successful in 2m41s
2025-01-06 14:53:27 +02:00
07e0d0ba3b fix script; bump version
Some checks failed
tagged-release / Tagged Release (push) Failing after 3m29s
2025-01-06 14:26:56 +02:00
93cae33bb0 fix: fix up script
Some checks failed
tagged-release / Tagged Release (push) Failing after 4m50s
2025-01-06 14:13:58 +02:00
b1e0db627c fix typings so they are usable for building the project itself
Some checks failed
tagged-release / Tagged Release (push) Failing after 3m24s
2025-01-06 14:02:07 +02:00
4fd05e9e6f fix up build scripts
Some checks failed
tagged-release / Tagged Release (push) Failing after 3m48s
2025-01-06 13:31:17 +02:00
de93adde8f fix: some source mapping fixes 2025-01-06 13:24:58 +02:00
5c68c1717c feat: implement some typed arrays 2025-01-06 13:24:44 +02:00
7883af7fff fix: some array function fixes 2025-01-06 13:24:09 +02:00
3d5be60fc7 feat: honor passed set in toReadableLines 2025-01-06 13:22:58 +02:00
ba0b4e06ad fix: some debugger issues 2025-01-06 13:22:26 +02:00
58e3546459 implement __proto__ field for all objects 2025-01-06 13:22:06 +02:00
57097e46ca reorganize libs 2025-01-06 13:20:51 +02:00
28e72503a6 Update .github/workflows/tagged-release.yml
Some checks failed
tagged-release / Tagged Release (push) Failing after 2m14s
2025-01-01 22:33:57 +00:00
d0a0796e14 Merge pull request 'topchetoeu-bump-0-10' (#32) from topchetoeu-bump-0-10 into master
Some checks failed
tagged-release / Tagged Release (push) Failing after 11m3s
Reviewed-on: #32
2025-01-01 22:20:16 +00:00
0de54e5505 Update gradle.properties 2025-01-01 22:19:56 +00:00
96c9d29a6a Update .github/workflows/tagged-release.yml 2025-01-01 22:19:36 +00:00
37dc844cc4 fix: use typescript instead 2025-01-01 22:16:01 +00:00
b97e4bf163 improve: UserValue API 2024-12-28 13:20:04 +02:00
9ce0504948 fix: debugger concurrency issues 2024-12-28 13:19:53 +02:00
4c53689d9c feat: some behavioral fixes 2024-12-28 13:19:35 +02:00
6c8c329992 feat: improve transpiler infrastructure 2024-12-28 13:19:02 +02:00
74f08b7483 small stdlib improvements
Some checks failed
tagged-release / Tagged Release (push) Failing after 7m8s
2024-12-28 13:17:45 +02:00
2bf920c681 remove old soruce map primordials and add next tick scheduler 2024-12-27 19:18:41 +02:00
1ac68425af feat: implement basic promises 2024-12-27 19:18:18 +02:00
7423e3f283 feat: more sophisticated stdlib definitions 2024-12-27 19:16:55 +02:00
afe4542682 fix: concurrency issues with debug server 2024-12-27 19:16:26 +02:00
c971fde0e2 feat: implement binary number literals 2024-12-27 19:15:20 +02:00
8a34db833c refactor: implement source mapping in javascript, instead of java 2024-12-27 19:15:09 +02:00
398d88c0a5 add more functions in stdlib, fix some issues 2024-12-27 19:10:56 +02:00
f80266618c fix: throw copied return syntax too closely 2024-12-27 19:09:01 +02:00
f1997e4584 fix: incorrect continue jump location in for loops 2024-12-25 03:01:49 +02:00
e93498fed5 fix typescript compiler script 2024-12-25 02:56:16 +02:00
29bea68525 feat: add back debugger from old version 2024-12-25 02:55:54 +02:00
3a6401094f fix: don't omit stack trace elements without locations 2024-12-25 02:54:22 +02:00
a705bf296e fix: empty file yields an error 2024-12-25 02:53:53 +02:00
c3432306f7 fix: global object can be an arbitrary value 2024-12-25 02:53:44 +02:00
d8c18ccf17 more utility 2024-12-25 02:53:24 +02:00
277f59e54a prepare codebase for source maps 2024-12-25 02:53:11 +02:00
a1bb5bdba6 separate the call stack from the debug context 2024-12-25 02:52:10 +02:00
c699102e56 try-catch AGAIN 2024-12-25 02:51:02 +02:00
b2f5068e12 some utility methods 2024-12-25 02:50:37 +02:00
05bafc7317 improve json parsing 2024-12-25 02:48:37 +02:00
c7c31c3859 feat: implement a lot of built-ins 2024-12-25 02:48:04 +02:00
c18015b9a0 fix: wrong precedences of && and ||
(how the fuck did i get this wrong)
2024-12-13 02:33:25 +02:00
5346b8917d refactor: add semicolons and spaces -> tabs in gradle scripts 2024-12-13 02:33:01 +02:00
2ed7959088 refacor: remove pesky console.log 2024-12-13 02:29:55 +02:00
239d0ae8d7 feat: implement a lot of stdlibs 2024-12-13 02:29:41 +02:00
6fac295b99 fix: try-catch broken (again) 2024-12-13 02:29:09 +02:00
66440a9649 fix: a plethora of loop off by one and null issues 2024-12-13 02:28:36 +02:00
274a925ff8 fix: broken labeled jumps 2024-12-13 02:27:57 +02:00
130fe17441 missed you lil fucker 2024-12-13 02:27:32 +02:00
493c54ed39 fix: symbol was falsy 2024-12-13 02:27:21 +02:00
75786f39ad fix: access of symbols in string 2024-12-13 02:27:12 +02:00
bce8b7293c fix: parseInt was broken 2024-12-13 02:26:34 +02:00
00aeef5321 fix: make member algorithms correct 2024-12-13 02:26:12 +02:00
ff4aa3dcfd fix: return null when loading an inexistent resource 2024-12-11 11:53:29 +02:00
45f52ff123 fix: catch variable causing issues 2024-12-11 11:53:19 +02:00
bed4014bef feat: implement simple env + ts loader 2024-12-11 11:53:02 +02:00
17406c6b81 feat: create build process for environment 2024-12-11 11:51:03 +02:00
3abdd8d3c9 feat: add constructor target support 2024-12-10 15:37:39 +02:00
a329f615cf fix: "e1" literal read as a number exponent 2024-12-10 01:21:44 +02:00
058d20b27f feat: dead simple Map and RegExp implementations 2024-12-10 01:11:07 +02:00
ea158c1e60 fix: converts symbol key to string when assigning function to member 2024-12-10 01:10:43 +02:00
4e18c76bb1 some toStrings 2024-12-10 01:10:14 +02:00
8d7939d85a fix: wrong index calculation 2024-12-10 01:09:50 +02:00
814e0d7b7e feat: allow to invoke compiler with variable encapsulation enabled 2024-12-10 00:44:18 +02:00
138baebacb fix: call regex constructor correctly 2024-12-10 00:43:56 +02:00
db241919f2 fix: when capturing, must check if already in captures 2024-12-10 00:43:15 +02:00
db45eb529d change indentation to tabs 2024-12-09 23:58:43 +02:00
caf44a50e5 Merge pull request #31 from TopchetoEU:TopchetoEU/revert-ES5
TopchetoEU/revert-ES5
2024-12-09 23:39:57 +02:00
65f9debecc fix: use default construct method 2024-12-09 23:39:05 +02:00
3f5e1a5fd8 feat: implement user values 2024-12-09 23:38:53 +02:00
b0d8a072aa add hashCode to primitives 2024-12-09 23:38:39 +02:00
2e8e123ec4 small parser fixes 2024-12-09 23:37:08 +02:00
54d55814af fix: errors with out of range arguments 2024-12-09 22:16:24 +02:00
28679f44d5 fix: symbols not stringified properly 2024-12-09 22:15:51 +02:00
611be55bbb fix: should throw engine exceptions, not java exceptions 2024-12-09 22:15:38 +02:00
4992d0211b fix: nasty issues with compilation 2024-12-09 22:15:15 +02:00
ba7505e148 fix: globalThis and for-in not parsed 2024-11-24 12:49:31 +02:00
3c13799c2f feat: make function logging configurable 2024-11-24 12:49:04 +02:00
5c2fd00bfb fix: add location data for LOAD_FUNCs 2024-11-24 12:48:49 +02:00
39eb6ffac5 fix: do variable inits properly 2024-11-24 12:48:30 +02:00
7f6df49fc5 fix: scope issues 2024-11-24 12:47:51 +02:00
61c5df5003 fix: gd damn it 2024-11-24 12:47:15 +02:00
41bb27e4dd implement all changes in runtime 2024-11-23 20:15:42 +02:00
b4e7a42975 regress: remove ES6 stuff (except apply and construct constraints) from funcs 2024-11-23 20:11:57 +02:00
92fb0dbbfd regress: simplify invoke model 2024-11-23 20:11:12 +02:00
fe8f65faf5 some final stuff in parsing 2024-11-23 20:10:47 +02:00
54fe16393a regress: remove infrastructure for super references 2024-11-23 20:10:11 +02:00
14e4aade35 regress: remove infrastructure needed for ES6 stuff, simplify loops 2024-11-23 20:09:29 +02:00
754648fbf6 regress: remove ES6 instructions 2024-11-23 20:08:01 +02:00
20f2c3c5e9 regress: remove ES6 stuff from members 2024-11-23 20:07:10 +02:00
c5067cbfdd regress: remove ES6 variables and simplify scope 2024-11-23 20:06:24 +02:00
5644966dd7 regress: remove ES6 nodes 2024-11-23 20:06:09 +02:00
50eb204da7 fix: remove unnecessary reference from core to compiler 2024-11-23 20:04:19 +02:00
45308e6d65 refactor: remove periods from ends of error msgs 2024-11-23 20:04:03 +02:00
0ebf189c95 fix: remove multi-key bullcrap 2024-11-23 20:01:00 +02:00
2fe5ce607a fix: multiply acting as subtract 2024-09-21 19:01:05 +03:00
d821a3a89b refactor: utilize inheritence in index.js 2024-09-21 18:46:22 +03:00
0064c74ac8 fix: don't allow execution of CALL_SUPER twice or in non-construct call 2024-09-21 18:46:02 +03:00
bd548c813a fix: null out thisArg only when constructing 2024-09-21 18:45:38 +03:00
78af69ec80 fix: parseStatementEnd behaving incorrectly when EOF 2024-09-21 18:44:08 +03:00
98e5299f9c fix: derived classes use the scope API incorrectly 2024-09-21 18:43:32 +03:00
797452c28f fix: tmp variables captured incorrectly 2024-09-21 18:43:15 +03:00
fee74dcba4 fix: infinite loop in class parser 2024-09-21 18:42:51 +03:00
9845a39e84 fix: operations polluting stack 2024-09-21 18:42:34 +03:00
ee78bdc1cb feat: implement derived classes 2024-09-21 18:06:03 +03:00
7fcb9ed19f fix: member field initializers should be iterable 2024-09-20 11:39:48 +03:00
8dee4353d4 feat: implement non-enumerable members in classes 2024-09-20 11:39:46 +03:00
59e6f34a01 refactor: clean up REPL stringification code 2024-09-20 11:39:40 +03:00
fdac93bf4d fix: scope offsets calculated incorrectly 2024-09-20 11:39:34 +03:00
06eae2eaf2 Merge pull request #29 from TopchetoEU/TopchetoEU/classes
Classes
2024-09-19 15:21:01 +00:00
d7b50fa45b refactor: use classes in index.js 2024-09-19 18:11:42 +03:00
077e8afff7 fix: some behavioral issues 2024-09-19 18:11:35 +03:00
631ef9db4a fix: differenciate between non-functions and non-invokables in messages 2024-09-19 18:10:50 +03:00
0258cc0a90 feat: implement classes (without inheritence) 2024-09-19 18:09:28 +03:00
0b3dca8b13 refactor: extract members into own classes 2024-09-19 18:08:11 +03:00
6d56660136 fix: stupid mistake with variable capturing 2024-09-19 14:22:21 +03:00
8a21873631 fix: retrofit patterns for bindings and check if var is init in runtime 2024-09-19 11:02:02 +03:00
fbbd26bf7d fix: remove unneeded comments 2024-09-14 22:08:33 +03:00
e2a8a382cc refactoring 2024-09-14 21:33:33 +03:00
0670ffcdd1 fix: int value not correctly recognized 2024-09-14 19:54:42 +03:00
9b957335bf optimization: keep StringValue instances tied to one String instance 2024-09-14 19:45:05 +03:00
e9f889576c feat: implement hidden integers 2024-09-14 19:38:30 +03:00
e11d182631 refactor: remove dead code 2024-09-14 18:52:07 +03:00
30674ee463 refactor: get rid of InterruptException 2024-09-14 18:46:47 +03:00
fab3e59910 feat: implement a byte array 2024-09-14 18:46:28 +03:00
d7e4e7a024 refactor: oops 2024-09-14 18:45:55 +03:00
e4166fe450 refactor: rewrite some code for java 8 compatibility 2024-09-14 18:45:20 +03:00
b5b7781136 Merge pull request #28 from TopchetoEU:TopchetoEU/destructing
TopchetoEU/destructing
2024-09-14 15:38:02 +03:00
f13bf584a5 feat: add some missing features in the polyfills 2024-09-14 15:25:34 +03:00
4e8b110fc4 feat: add assign shorthands 2024-09-14 14:33:09 +03:00
cb82f4cf32 feat: implement patterns 2024-09-14 14:23:46 +03:00
23ae2b2e46 todo 2024-09-14 14:23:35 +03:00
55613ef2c9 feat: extend the instruction set 2024-09-14 14:23:28 +03:00
0b34c68139 fix: unnecessary new line in toReadable 2024-09-14 14:22:51 +03:00
d87e53264d refactor: split array logic 2024-09-14 14:22:31 +03:00
1f42263051 clean up member logic 2024-09-14 13:53:58 +03:00
3e6816cb2c fix: properly hande variable collisions 2024-09-12 20:25:11 +03:00
2a01b3d86e some work losl 2024-09-07 21:06:08 +03:00
8e64d13c87 refactor: clean up assigning 2024-09-06 15:48:22 +03:00
5f88061ee7 refactor: rename callNew -> construct and call -> invoke 2024-09-06 15:48:07 +03:00
b9268518f6 fix: array statements broken when empty elements 2024-09-06 15:46:10 +03:00
63ccd5757e feat: implement spread_obj intrinsic 2024-09-06 10:03:55 +03:00
515011b3ef refactor: improve meber listing system 2024-09-06 10:03:37 +03:00
b6f04aa177 Merge pull request #27 from TopchetoEU/TopchetoEU/optimize-var-flatten
Optimize var flattening
2024-09-05 22:32:48 +03:00
6f548ce5ff feat: reflect scope optimizations in runtime 2024-09-05 22:30:28 +03:00
7c74df4d36 refactor: use new system to reorder variables that overlaps neighboring scopes 2024-09-05 21:25:39 +03:00
641d4d1863 Merge pull request #26 from TopchetoEU/ES6
ES6 Support Groundwork + Fixes
2024-09-05 17:26:07 +03:00
0004839f6f fix: realloc for declarations after each iteration 2024-09-05 17:15:26 +03:00
07411f62c8 feat: implement capturable locals realloc 2024-09-05 17:14:59 +03:00
4bfc062aaf fix: correctly flatten locals in control flow statements 2024-09-05 17:13:34 +03:00
9ec99def3f fix: variable declarations shouldn't collide with defined name of named function exp 2024-09-05 13:29:42 +03:00
8f13ff3e0b fix: scope gets polluted by arguments with named function expressions 2024-09-05 13:29:20 +03:00
d7353e19ed fix: treat "arguments" as a keyword (as per strict soecifications) 2024-09-05 13:18:53 +03:00
e509edc459 fix: wrong arguments when compilling function bodies 2024-09-05 13:03:49 +03:00
b6a90b108b fix: wrong check for var decl collisions 2024-09-05 13:03:36 +03:00
55caf1e206 refactor: remove unneeded old comments 2024-09-05 13:03:04 +03:00
eac4a3af23 fix: throw access before decl for const and let in runtime 2024-09-05 00:31:03 +03:00
5b4adf5286 a clusterfuck of fixes with let and const 2024-09-05 00:28:13 +03:00
807b3918fa motherfucker 2024-09-04 15:55:59 +03:00
9265a7d813 Merge branch 'master' into ES6 2024-09-04 15:52:03 +03:00
1589ef51b0 refactor: move src and resources to standard places 2024-09-04 15:32:54 +03:00
marregui
313b20a3b3 Add first test (#23)
* Add the first test and upgrade build.gradle to modern standards

1. gradle wrapper
2. ./gradlew run
3. manifest will look like
    Manifest-Version: 1.0
    Main-Class: me.topchetoeu.jscript.runtime.SimpleRepl
    Build-Timestamp: 2024-09-04T10:44:35.990+0200
    Build-Branch: ma/add-first-tests
    Build-Revision: 412edc0ebc
    Build-Jdk: 21.0.3 (Oracle Corporation 21.0.3+7-LTS-152)
    Build-Author: TopchetoEU
4. build/distributions contains a zip and a jar which contain jscript-0.9.41-beta.jar
5. unnecessary libs have been removed
6. gradle has been updated to 8.10
7. first test has been added

* fix: revert removal of Jabel (for support of Java 11)

---------

Co-authored-by: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com>
2024-09-04 15:29:17 +03:00
ce9b419757 reafactor: make globals initializer use ES6 features 2024-09-04 10:45:34 +03:00
6f8efe74c4 fix: print was returning java null, not JS undefined 2024-09-04 10:45:14 +03:00
bd503ed943 feat: impl new instructions 2024-09-04 10:44:58 +03:00
5359c54694 fix: rethrow SyntaxException from compilation as EngineException 2024-09-04 10:44:44 +03:00
93c246ad97 refactor: remove arg loading from frame 2024-09-04 10:44:24 +03:00
7c8efaf066 fix: incorrect printing of object-like values 2024-09-04 10:43:40 +03:00
546d663466 feat: add this arg capture 2024-09-04 10:42:21 +03:00
f929015f55 ammend to prev commit 2024-09-04 10:41:52 +03:00
2a5f6aa9aa fix: use _STR and _INT variants of member instructions 2024-09-04 10:41:17 +03:00
78d233a6bd refactor: remove ArgumentsNode 2024-09-04 10:39:11 +03:00
3f25868f19 fix: some scope bug fixes 2024-09-04 10:38:16 +03:00
e3f1bc0949 fix: incorrect compilation of if-else 2024-09-04 10:36:48 +03:00
506726fd76 feat: implement ES6 variables and rest args 2024-09-04 10:36:25 +03:00
4cbc108686 feat: implement optional arguments 2024-09-04 10:00:48 +03:00
c39c06b792 refactor: move away intrinsic logic to final methods for performance 2024-09-04 10:00:45 +03:00
7ab78b9cea fix: control flow nodes were making scopes instead of compound nodes 2024-09-04 10:00:43 +03:00
87e077d70d oops 2024-09-04 10:00:40 +03:00
52f7c15ac8 refactor: change how function scope keeps track of arguments 2024-09-04 10:00:38 +03:00
6932bea677 refactor: remove unused class 2024-09-04 10:00:36 +03:00
82d6f52a26 refactor: make some classes final for performance 2024-09-04 10:00:33 +03:00
1b87c2f5a6 fix: add for-of to statement list 2024-09-04 10:00:30 +03:00
163dfe7b6e feat: implement access to intrinsics 2024-09-04 10:00:25 +03:00
2b6d4a87ca fix: for in and for of not reading open paren 2024-09-04 10:00:22 +03:00
349d392269 major rewrite: clean up a lot of code and lay ground for ES6 support 2024-09-04 10:00:15 +03:00
6481e992fa feat: implement "has" function for scopes 2024-09-04 10:00:11 +03:00
4a5e5a71af feat: Create new scope system for ES6+ support 2024-09-04 10:00:02 +03:00
89ba921b4a refactor: rename statements to nodes 2024-09-04 10:00:00 +03:00
a45f4109d8 feat: add environment in Source 2024-09-04 09:59:57 +03:00
62aba62a41 refactor: make Environment more reusable 2024-09-04 09:59:54 +03:00
4048d6ef1c refactor: rename ES5 to JavaScript 2024-09-04 09:59:42 +03:00
d0ccf00f14 everything all at once 2024-09-04 09:59:35 +03:00
f09feae08f refactor: clean up parsing 2024-09-04 09:59:32 +03:00
ef0fc5a61d refactor: distribute parse functions in node classes 2024-09-04 09:59:28 +03:00
bab59d454f refactor: Transition to a Value class 2024-09-04 09:59:26 +03:00
3475e3a130 refactor: Remove environment-related bloat 2024-09-04 09:59:15 +03:00
49b52d90a7 fix: wrappers cache compare objects with .equals and not == 2024-04-21 11:03:00 +03:00
8a8de518a6 feat: make Function constructor 2024-04-20 23:44:02 +03:00
099201e4ad refactor: remove testing junk in REPL 2024-04-20 22:23:45 +03:00
f8553b79f9 fix: run module in an isolated context 2024-04-20 22:22:55 +03:00
ba6462458c fix: some fixes in the filesystem 2024-04-20 22:18:47 +03:00
e33cdbb172 fix: properties not applied to wrappers without constructor method 2024-04-13 01:03:34 +03:00
fc6ddf7d3c feat: allow interface proxy wrappers 2024-04-12 16:37:06 +03:00
7f275095a2 fix: continue statement compiled incorrectly 2024-04-07 12:50:58 +03:00
90d019f92a bump 2024-04-07 12:33:48 +03:00
6fb31be12c fix(debugger): handle all errors when generating description 2024-04-07 12:33:26 +03:00
d6ede0b404 fix: incorrect toFixed behavior 2024-04-03 15:52:01 +03:00
71b40240c0 feat: add Number.toFixed 2024-04-03 15:09:01 +03:00
a8775d212f fix: clean up extensions at some points 2024-04-03 14:52:29 +03:00
71872a8d64 fix 2024-04-03 14:25:14 +03:00
c707f880f7 fix: use Extensions instead of Environment 2024-04-03 14:21:23 +03:00
0d629a6e82 fix: use correct class instead of proxy 2024-04-03 12:27:15 +03:00
6eea342d04 fix: fuck 2024-04-02 18:24:43 +03:00
ece9cf68dc fix: correctly update proto chain 2024-04-02 18:19:05 +03:00
11ecd8c68f fix: exec debugger close logic on application exit 2024-04-02 18:05:49 +03:00
48bd304c6e fix: environment forks fixes 2024-04-02 18:05:20 +03:00
d8e46c3149 fix: clone environment correctly 2024-03-31 16:11:32 +03:00
5fc5eb08f8 fix: update breakpoints when removing bp 2024-03-30 12:52:44 +02:00
8acbc003c4 fix: properly resolve breakpoints 2024-03-30 12:13:04 +02:00
fda33112a7 fix: load maps when attaching debugger 2024-03-30 11:13:45 +02:00
67b2413d7c bump2 2024-03-30 10:36:55 +02:00
3a05416510 bump 2024-03-30 10:30:26 +02:00
c291328cc3 fix: detach debugger after close 2024-03-30 10:22:12 +02:00
7cb267b0d9 fix: some issues with debugger 2024-03-30 09:55:20 +02:00
4e31766665 fix: add new vscode debugger functions 2024-03-29 21:53:15 +02:00
b5b63c4342 fix: make global cache of native wrappers 2024-03-28 16:08:07 +02:00
18f70a0d58 fix: i hate wrappers 2024-03-28 15:10:21 +02:00
d38b600366 fix: some more wrapper issues 2024-03-28 14:52:49 +02:00
0ac7af2ea3 fix: take into account empty classes 2024-03-28 14:21:23 +02:00
5185c93663 fix: don't include non-exposing wrappers in proto chain
feat: allow adding custom wrappers
2024-03-28 00:57:09 +02:00
510422cab7 feat: implement logic for exposing non-static fields 2024-03-27 23:39:33 +02:00
79e1d1cfaf Merge branch 'master' of https://github.com/TopchetoEU/java-jscript 2024-03-27 23:08:25 +02:00
e0f3274a95 feat: add simple for-of loop (not intended for production usage) 2024-03-27 23:08:21 +02:00
ef5d29105f Update README.md 2024-03-10 02:17:18 +02:00
d8ea6557df fix: buildline expects tag to start with 'v' 2024-03-09 00:45:00 +02:00
5ba858545a fix: defer handles of async functions 2024-03-09 00:28:30 +02:00
446ecd8f2b fix: promise defers callback twice 2024-03-08 17:23:50 +02:00
fbf103439a bump 2024-03-08 16:55:46 +02:00
b30f94de8f refactor: move function pushMsg signatures in EventLoop 2024-03-08 16:53:47 +02:00
47b4dd3c15 refactor: rename code to runtime 2024-03-06 23:23:01 +02:00
0fb336373a fix: make fs calls synchronized 2024-03-06 12:50:57 +02:00
b33325a98d fix: clear buffer of line writer file 2024-03-05 17:10:06 +02:00
ccf75d6066 fix: don't use Context.NULL in global scope 2024-03-05 16:51:50 +02:00
662dcc1ac1 bump 2024-03-05 16:30:13 +02:00
3e6214659b fix: use new global API 2024-03-05 15:54:51 +02:00
7c6622c53d fix: separate scope records from scopes 2024-03-05 15:45:02 +02:00
70d5871091 fix: properly check permissions 2024-03-03 20:47:54 +02:00
7b9bbe576b feat: add std streams as global variables 2024-03-03 20:31:20 +02:00
e6399c1546 refactor: remove unused var 2024-03-02 14:01:58 +02:00
c8253795b2 fix: make debugging work again 2024-03-02 13:56:48 +02:00
49dd725669 refactor: fully separate event loop from context 2024-02-29 00:23:14 +02:00
52489ad3a8 feat: separate compilation and runtime 2024-02-26 13:22:56 +02:00
c4d44547c8 fix: call move when passing same array to copyTo 2024-02-21 11:03:19 +02:00
c6dc031cfd fix: respect return value of constructors 2024-02-21 11:01:33 +02:00
285960bdd6 refactor: rework fs error system 2024-02-09 13:46:57 +02:00
cf99845f6b refactor: rework permission system 2024-01-13 11:05:43 +02:00
48bd1e2015 bump 2024-01-12 09:54:32 +02:00
304665904f feat: extract log API to console 2024-01-12 09:53:56 +02:00
56ae3a85a6 build: improve build scripts 2024-01-12 09:48:20 +02:00
0178cb2194 build: specify java toolchain 2024-01-11 11:46:51 +02:00
a2cb5cd473 build: add gradle wrapper props 2024-01-11 11:46:41 +02:00
c123427e77 bump 2024-01-11 10:59:07 +02:00
7ac5ded185 build: set main class in jar manifest 2024-01-11 10:58:40 +02:00
769d6ae8fc version unbump 2024-01-11 10:55:43 +02:00
afb99ffc70 action attempt 1 2024-01-11 10:52:18 +02:00
46136e77e2 build: improve build scripts 2024-01-11 10:47:41 +02:00
b460b87318 refactor: remove old build script 2024-01-11 09:57:41 +02:00
e772f0b50d feat: change custom build script to gradle 2024-01-11 09:56:50 +02:00
187ad55291 fix: fully remove typescript 2024-01-10 17:02:45 +02:00
8156a1733f refactor: move debugging logic out of core 2024-01-10 17:01:24 +02:00
d1937fdb63 remove typescript 2024-01-10 16:51:40 +02:00
3f826cc85d remove old test 2024-01-10 16:51:17 +02:00
af35d7f20b fix: oops 2024-01-10 11:25:29 +02:00
cfa0e001b9 refactor: split up code in 4 modules 2024-01-10 11:21:49 +02:00
c10d071346 feat: some API improvements 2024-01-10 11:21:35 +02:00
89eea7d62b refactor: remove old useless exceptions 2024-01-06 19:51:48 +02:00
18d22a1282 Merge pull request #12 from TopchetoEU/TopchetoEU/cleanup
Major codebase cleanup
2024-01-06 18:28:11 +02:00
72a0d39d0b fix: make java 11 compatible 2024-01-06 18:27:36 +02:00
d8585a20bf refactor: don't require ctx in frame.push 2024-01-06 18:23:34 +02:00
e4c9a8756e fix: debugger hanging sometimes 2024-01-06 18:23:20 +02:00
c6e6425c7e fix: bring back ts minification 2024-01-06 17:53:42 +02:00
292ca64cb9 fix: wrong behavior in Number.toString (which somehow broke typescript) 2024-01-06 17:50:06 +02:00
4572db5c46 feat: some array tests 2024-01-06 17:49:36 +02:00
0251c4689d fix: use Values to access members in ObjectLib, instead of direct access 2024-01-06 17:49:27 +02:00
3173919b49 fix: implement proper parseInt logic 2024-01-06 17:48:35 +02:00
45f133c6b0 fix: use Values.call instead of direct calling 2024-01-06 17:48:10 +02:00
34276d720c fix: remove sparse call arguments 2024-01-06 17:47:38 +02:00
2c634778c3 fix: report proper function name in String.length errors 2024-01-06 17:47:07 +02:00
4aa757e625 fix: Function.bind now passess this argument, instead of the function itself 2024-01-06 17:46:39 +02:00
918f2623cd fix: small issue with sparse arrays 2024-01-06 17:46:13 +02:00
a321fc14bc fix: wrong signature of Map.forEach 2024-01-06 17:45:56 +02:00
07a6f18b16 refactor: some spring cleaning in array lib, fix small issue with join 2024-01-06 17:45:52 +02:00
5f4011aa0c refactor: move NO_RETURN to Values, remove some casters from Values 2024-01-06 17:45:44 +02:00
71f735b812 fix: some more libs fixes 2024-01-04 13:58:04 +02:00
e575b3287e fix: try-catch-finally fix #457846982 2024-01-04 13:57:41 +02:00
4fa5f5a815 feat: use new wrapper API in libs 2024-01-04 10:02:14 +02:00
a61c6a494e fix: some Argument and Engine API improvements, 2024-01-04 10:02:01 +02:00
978ee8db79 feat: make better native wrapper API 2023-12-28 16:55:57 +02:00
e372941e99 refactor: generalize Reading class 2023-12-27 20:18:41 +02:00
c36a0db860 refactor: remove more dead code 2023-12-27 20:18:23 +02:00
d6ee59363f refactor: remove unneeded event system 2023-12-27 20:10:11 +02:00
d5fd6e650e fix: clean up debugger API 2023-12-27 20:02:45 +02:00
c0b895e00a feat: greatly improve Context API 2023-12-27 14:22:18 +02:00
9ea5cd9277 refactor: remove old Data API 2023-12-27 14:21:52 +02:00
aaf9a6fa45 fix: pass environment to compiler via simple environment wrapper 2023-12-27 13:59:19 +02:00
579f09c837 fix: pass arguments to regex constructor in LOAD_REGEX 2023-12-27 13:28:17 +02:00
3343262e72 fix: main now uses new env API 2023-12-27 13:16:21 +02:00
153a1a9a49 feat: readd permissions API via new env API 2023-12-27 13:16:12 +02:00
bf38587271 feat: readd module API via env API 2023-12-27 13:15:46 +02:00
21534efd60 feat: readd FS API via new env API 2023-12-27 13:14:46 +02:00
802f2f3f52 fix: access env via context 2023-12-27 13:14:09 +02:00
38acc20a6f refactor: greatly improve Environment API 2023-12-26 17:12:20 +02:00
d7f6010319 fix: make code java 11 compatible 2023-12-26 14:22:35 +02:00
87f8975275 build: minify typescript code 2023-12-26 14:22:25 +02:00
09eb6507dc Merge pull request #11 from TopchetoEU/TopchetoEU/modules
Module support
2023-12-26 14:20:55 +02:00
2f58f6b245 feat: implement basic module system 2023-12-26 14:12:41 +02:00
4bc363485f fix: losts of FS API improvements 2023-12-26 14:02:32 +02:00
8e01db637b fix: improve path resolutions and FS API 2023-12-26 11:27:40 +02:00
1c64912786 feat: create memory-light directory list file 2023-12-26 11:27:09 +02:00
28265a8f44 fix: some bug fixes and improvements with File interface 2023-12-26 11:26:08 +02:00
e9e020512e fix: environment pushed when it shouldn't be 2023-12-24 15:17:38 +02:00
4b0bbf5190 fix: correctly convert virtual to real path 2023-12-24 14:40:27 +02:00
031f78ebf1 fix: don't include ts's code in repo anymore 2023-12-24 14:32:37 +02:00
562f1f9425 fix: remove major mistakes in README 2023-12-24 14:31:50 +02:00
82a09e8865 fix: some config files cleanup 2023-12-24 14:31:33 +02:00
90da2db1fb fix: error now is not null-prototyped 2023-12-24 14:30:48 +02:00
3d275c52c0 refactor: fix code to not result in compile warning 2023-12-24 14:30:31 +02:00
797585f539 feat: implement some stdlib functions 2023-12-24 14:28:12 +02:00
7a301eba8f fix: some stupid mistakes in FS 2023-12-24 14:27:27 +02:00
1b2068a274 fix: never-ending try-catch issues 2023-12-24 14:27:00 +02:00
078d7ed95f refactor: improve Engine API 2023-12-24 14:26:42 +02:00
93973c12b1 fix: change default return type to void for generators 2023-12-24 14:10:03 +02:00
cad4f34b51 fix: use correct env declarations in bootstrap.js 2023-12-24 14:08:20 +02:00
d3571d6ee2 fix: make .gitignore more restrictive 2023-12-22 10:46:30 +02:00
caf9131cde refactor: replace all CRLF with LF 2023-12-22 10:45:04 +02:00
8c6379eb24 Merge pull request #10 from TopchetoEU/TopchetoEU/mapping
Add support for source mappings
2023-12-18 22:42:38 +02:00
380a5c720a feat: make environment hidable from stack trace 2023-12-18 22:38:26 +02:00
76c3d377af feat: use mappings in stack traces 2023-12-18 22:30:14 +02:00
42f443572a fix: how the hell did i fix this (it was the cache all along) 2023-12-18 22:11:08 +02:00
773bc72f3e refactor: rename compileWithDebug to compile 2023-12-14 21:07:16 +02:00
0b5178e9fd fix: return minified typescript 2023-12-14 21:06:23 +02:00
8cffcff7db refactor: remove unused instructions 2023-12-14 17:02:24 +02:00
60bbaaccd4 fix: some issues with try-catch 2023-12-14 16:49:51 +02:00
60b1762462 refactor: remove printf 2023-12-14 16:49:35 +02:00
34434965d2 cant be fucked to split this one up 2023-12-14 12:39:01 +02:00
fe86123f0f refactor: improve function statement 2023-11-29 13:04:41 +02:00
d5e6edfa8b refactor: replace .locate with argument 2023-11-29 13:03:53 +02:00
73345062ca feat: implement new API with source maps 2023-11-28 21:40:37 +02:00
124341969c fix: simplify source map API 2023-11-28 21:39:25 +02:00
8defd93855 fix: use proper name for native constructors 2023-11-28 21:37:46 +02:00
6c57e0e9f2 fix: properly handle wrapper function 2023-11-28 21:37:13 +02:00
f1932914ee fix: move debugger assets to correct location 2023-11-27 20:28:02 +02:00
977701e601 feat: implement source maps 2023-11-26 16:50:07 +02:00
e8a7ac8da8 refactor: reorganize assets 2023-11-26 16:49:47 +02:00
6b1cb852c2 fix: arrays wrongly stringified in JSONLib 2023-11-26 13:51:56 +02:00
b59a003086 feat: implement VLQ parsing 2023-11-26 13:51:43 +02:00
1902e41f61 feat: make typescript output mappings 2023-11-26 11:52:47 +02:00
27162ef8ac feat: improve Bufffer API 2023-11-26 11:51:32 +02:00
4f22e76d2b fix: make code java 11 compatible 2023-11-25 20:22:16 +02:00
8924e7aadc build: fix build script to exit with code when error occurs 2023-11-25 20:16:06 +02:00
1d0e31a423 Merge pull request #9 from TopchetoEU/TopchetoEU/perms-and-fs
Permissions and filesystems
2023-11-25 20:10:59 +02:00
ab56908171 fix: faulty insufficient permissions error 2023-11-25 20:09:59 +02:00
eb14bb080c fix: debugger breakpoints not registered 2023-11-25 20:09:47 +02:00
f52f47cdb4 feat: properly implement filesystems 2023-11-25 19:36:18 +02:00
567eaa8514 fix: incorrect declarations 2023-11-25 19:13:20 +02:00
2cfdd8e335 feat: add utf8 encoding and decoding 2023-11-25 19:12:56 +02:00
4b1ec671e2 fix: micro tasks not handled properly 2023-11-25 18:58:35 +02:00
b127aadcf6 fix: promise was sending macro tasks instead of micro 2023-11-25 18:58:24 +02:00
b6eaff65ca style: remove unnececeary import 2023-11-25 18:48:46 +02:00
443dc0ffa1 feat: add await function to promise 2023-11-25 18:47:51 +02:00
e107dd3507 fix: promise doesn't use microtasks in some cases 2023-11-25 18:47:36 +02:00
6af3c70fce feat: add filesystem libs 2023-11-25 18:47:01 +02:00
8b743f49d1 feat: add toString, equals and hashCode overrides to wrappers and objects 2023-11-25 18:46:13 +02:00
e1ce384815 feat: add parse function to Filename 2023-11-25 18:44:27 +02:00
86d205e521 fix: parsing files won't modify last instruction to RET, instead will just append it 2023-11-25 18:44:11 +02:00
f0ad936e5b refactor: use new iterable names 2023-11-25 18:43:43 +02:00
4a1473c5be fix: sorting with no comparator now works 2023-11-25 18:42:54 +02:00
4111dbf5c4 fix: functions now print their name when stringified 2023-11-25 18:41:47 +02:00
1666682dc2 refactor: get rid of data parent hierarchy 2023-11-25 18:41:18 +02:00
f2b33d0233 feat: add support for async iterators
fix: comparasions had small behavioural issue
2023-11-25 18:40:49 +02:00
f5a0b6eaf7 fix: some improvements of compiled bytecode 2023-11-25 18:38:43 +02:00
829bea755d fix: CodeFrame didnt't handle out of bounds jumps well 2023-11-25 18:38:16 +02:00
4b0dcffd13 feat: add fs declarations in lib.d.ts 2023-11-25 18:37:40 +02:00
987f8b8f00 fix: handle native errors properly-ish 2023-11-25 14:18:46 +02:00
55e3d46bc2 feat: implement memory and root fs 2023-11-25 14:18:04 +02:00
3e25068219 feat: implement bulk of fs 2023-11-25 14:17:37 +02:00
7ecb8bfabb feat: implement permissions 2023-11-25 14:16:02 +02:00
488deea164 fix: improve performance of typescript by caching separate declarations 2023-11-14 09:24:39 +02:00
ed08041335 fix: internal error when trying to use key of "undefined" 2023-11-14 09:24:00 +02:00
0a4149ba81 fix: remove double space in "Uncaught ..." 2023-11-14 09:23:15 +02:00
30f5d619c3 fix: errors now have the right prototype and name 2023-11-14 09:22:56 +02:00
e7dbe91374 refactor: clean up protocol.json 2023-11-13 20:06:07 +02:00
455f5a613e feat: implement simple permission matching 2023-11-13 19:09:33 +02:00
1eeac3ae97 fix: replace templates in Metadata class with placeholder data 2023-11-13 19:08:56 +02:00
1acd78e119 refactor: clean up Main class 2023-11-13 18:56:59 +02:00
df9932874d feat: remove unnececeary @NativeInit directives 2023-11-06 14:03:15 +02:00
b47d1a7576 refactor: remove StackData and Data usage 2023-11-06 13:53:36 +02:00
fdfa8d7713 refactor: some minor fixes, rewrite README example 2023-11-06 10:55:38 +02:00
f5d1287948 fix: some annoying bugs, as well as splice 2023-11-06 00:55:30 +02:00
15f4278cb1 fix: build with java 17 2023-11-05 21:00:39 +02:00
df8465cb49 Merge pull request #8 from TopchetoEU/TopchetoEU/tests
Integrate typescript
2023-11-05 20:32:42 +02:00
e3104c223c fix: remove some unnececeary logs 2023-11-05 20:29:21 +02:00
1d0bae3de8 feat: include typescript code in source code 2023-11-05 20:27:23 +02:00
b66acd3089 fix: several more fixes 2023-11-05 19:44:44 +02:00
e326847287 feat: send value stack to debug client 2023-11-05 19:44:35 +02:00
26591d6631 fix: lazy operators incorrectly pop values from stack 2023-11-05 19:44:08 +02:00
af31b1ab79 fix: several small fixes 2023-11-05 19:43:53 +02:00
f885d4349f refactor: fix some bad code >:( 2023-11-05 19:43:28 +02:00
d57044acb7 fix: several bug fixes to help with typescript support 2023-11-05 12:44:29 +02:00
7df4e3b03f fix: oops 2023-11-04 11:43:57 +02:00
ed1009ab69 refactor: some code restructuring in the debugging 2023-11-04 11:42:06 +02:00
f856cdf37e fix: messages larger than 64KB are now fragmented properly 2023-11-04 11:41:31 +02:00
4f82574b8c fix: various small behavioural issues
fix: pesky try-catch logic
2023-11-04 11:40:50 +02:00
0ae24148d8 feat: write some tests 2023-11-04 11:38:48 +02:00
ac128d17f4 feat: implement Array.reduce
fix: native functions are now named
2023-11-04 11:38:29 +02:00
6508f15bb0 refactor: remove typescript source code from repo (for now) 2023-11-04 11:37:13 +02:00
69f93b4f87 refactor: make filenames more consistent 2023-11-04 11:36:36 +02:00
b675411925 fix: a lot of minor bugs 2023-10-29 23:47:48 +02:00
d1e93c2088 Merge branch 'master' of https://github.com/TopchetoEU/java-jscript 2023-10-29 12:33:18 +02:00
942db54546 feat: improve vscode debugging compatibility 2023-10-29 12:30:43 +02:00
d20df66982 feat: improve vscode debugging compatibility 2023-10-29 12:25:33 +02:00
16a9e5d761 Merge pull request #7 from TopchetoEU/TopcehtoEU/debugging
Debugging support
2023-10-28 17:11:55 +03:00
dc9d84a370 chore: nothing of use 2023-10-28 17:10:27 +03:00
edb71daef4 feat: fully implement local and capture scope object wrappers
feat: implement object sending and receiving
2023-10-28 16:58:33 +03:00
4b84309df6 feat: complete steptrough and breakpoints in debugger 2023-10-27 15:12:14 +03:00
d2d9fa9738 fix: exit now works 2023-10-07 14:08:47 +03:00
a4e5f7f471 refactor: clean up useless catch blocks 2023-10-07 13:55:44 +03:00
cc044374ba refactor: replace InterruptedException with unchecked alternative 2023-10-07 13:42:55 +03:00
517e3e6657 refactor: clean up context and stack data 2023-10-07 13:07:07 +03:00
926b9c17d8 feat: readd JSON to lib 2023-10-06 18:42:35 +03:00
fc705e7383 feat: readd date internals 2023-10-06 12:03:33 +03:00
a17ec737b7 refactor: rename polyfills to libs 2023-10-06 11:57:29 +03:00
952a4d631d fix: keep order of object fields 2023-10-04 22:06:49 +03:00
005610ca40 Merge pull request #6 from TopchetoEU/TopchetoEU/function-optimizations
Cache function bodies
2023-10-04 21:38:29 +03:00
9743a6c078 fix: cache function bodies 2023-10-04 21:34:33 +03:00
21a6d20ac5 refactor: some code cleanup, emit better bytecode 2023-10-04 12:16:06 +03:00
4d1846f082 fix: reconfigure workflow and build.js 2023-10-04 09:00:44 +03:00
9547c86b32 Merge pull request #5 from TopchetoEU/TopchetoEU/corelib-reprogramming
Core library reprogramming
2023-10-04 08:50:26 +03:00
1cccfa90a8 refactor: remove pesky node files 2023-10-04 08:49:49 +03:00
604b752be6 feat: add global functions 2023-10-04 08:49:20 +03:00
6c7fe6deaf feat: add old data to both subcontexts 2023-10-04 08:30:33 +03:00
68f3b6d926 feat: fully remove typescript init code 2023-10-04 08:10:26 +03:00
63b04019cf feat: implement timers in java 2023-09-28 10:25:32 +03:00
9c65bacbac fix: type name can f itself 2023-09-28 09:38:51 +03:00
0dacaaeb4c a lot of fixes 2023-09-27 15:08:23 +03:00
c1b84689c4 fix: rewrite try-catch-finally logic 2023-09-27 10:27:19 +03:00
bd08902196 fix: revert back to old error protocol 2023-09-27 10:27:04 +03:00
22ec95a7b5 feat: implement errors 2023-09-26 22:54:12 +03:00
6c71911575 feat: implement symbols in java 2023-09-26 11:29:49 +03:00
e16c0fedb1 feat: implement map and set polyfills in java 2023-09-26 08:31:27 +03:00
cf36b7adc5 fix: some string polyfill fixes 2023-09-25 18:33:07 +03:00
ff9b57aeb9 fix: remove string from core.ts 2023-09-25 18:32:59 +03:00
47c62128ab feat: implement string polyfill in java 2023-09-25 18:18:36 +03:00
4aaf2f26db feat: add some standard functions to Number 2023-09-24 20:50:53 +03:00
f21cdc831c fix: primitive values will be native classes now 2023-09-24 14:15:12 +03:00
86b206051d feat: implement bool polyfills in java 2023-09-24 13:49:37 +03:00
f2cd50726d feat: implement array polyfills in java 2023-09-24 12:56:53 +03:00
d8071af480 refactor: remove dead .ts code 2023-09-21 10:50:48 +03:00
0b7442a3d8 fix: remove raw native funcs, use this arg passing instead 2023-09-21 10:50:03 +03:00
356a5a5b78 feat: a lot of typescript corelibs translated to java 2023-09-20 23:02:23 +03:00
da4b35f506 feat: Create filesystem interface and a physical filesystem 2023-09-18 10:31:50 +03:00
8c049ac08f fix: forgor to delete this 2023-09-09 19:02:55 +03:00
334 changed files with 18408 additions and 12717 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* -text

View File

@@ -3,7 +3,7 @@ name: "tagged-release"
on:
push:
tags:
- "v*"
- "*"
jobs:
tagged-release:
@@ -12,18 +12,23 @@ jobs:
steps:
- name: Clone repository
uses: GuillaumeFalourd/clone-github-repo-action@main
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
owner: 'TopchetoEU'
repository: 'java-jscript'
- name: "Build"
run: |
cd java-jscript; node ./build.js release ${{ github.ref }}
- uses: "marvinpinto/action-automatic-releases@latest"
distribution: 'adopt'
java-version: '17'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
cache-disabled: true
gradle-version: "8.10"
- name: Build
run: gradle build
- name: Create release
uses: "https://gitea.com/actions/gitea-release-action@main"
with:
# api_key: "${{secrets.TOKEN}}"
files: |
java-jscript/LICENSE
java-jscript/dst/*.jar
LICENSE
build/libs/*.jar

30
.gitignore vendored
View File

@@ -1,11 +1,19 @@
.vscode
.gradle
.ignore
/out
/build
/bin
/dst
/*.js
!/build.js
/dead-code
/Metadata.java
/*
!/src
!/doc
!/tests
!/.github
!/.gitignore
!/.gitattributes
!/LICENSE
!/README.md
!/settings.gradle
!/build.gradle
!/gradle.properties
!/package.json
!/rollup.config.js
!/tsconfig.json

View File

@@ -2,33 +2,24 @@
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.**
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes.
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
## Example
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`:
The following is going to execute a simple javascript statement:
```java
var engine = new PolyfillEngine(new File("."));
var in = new BufferedReader(new InputStreamReader(System.in));
engine.start();
var engine = new Engine();
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
var env = Internals.apply(new Environment());
while (true) {
try {
var raw = in.readLine();
// Queue code to load internal libraries and start engine
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
// Run the engine on the same thread, until the event loop runs empty
engine.run(true);
var res = engine.pushMsg(false, engine.global(), Map.of(), "<stdio>", raw, null).await();
Values.printValue(engine.context(), res);
System.out.println();
}
catch (EngineException e) {
try {
System.out.println("Uncaught " + e.toString(engine.context()));
}
catch (InterruptedException _e) { return; }
}
catch (IOException | InterruptedException e) { return; }
}
// Get our result
System.out.println(awaitable.await());
```

128
build.gradle Normal file
View File

@@ -0,0 +1,128 @@
import java.text.SimpleDateFormat
plugins {
id 'application';
id 'com.github.node-gradle.node' version '5.0.0';
id 'net.nemerosa.versioning' version '2.15.0';
id 'org.ajoberstar.grgit' version '5.0.0-rc.3'; // required by gradle
// TODO: figure out how to integrate proguard
// id "com.github.xaverkapeller.proguard-annotations"
}
base.archivesName = project.project_name;
version = project.project_version;
group = project.project_group;
description = 'ES5-compliant JavaScript interpreter';
node {
version = '20.0.0';
npmVersion = '8.0.0';
download = true;
}
task compileEnv(type: NpmTask) {
dependsOn npmInstall;
inputs.files('rollup.config.js');
inputs.dir('src/lib/libs');
outputs.files("build/js/env.js");
// group = 'build'
args = ['run', 'build-env'];
}
task compileTypescript(type: NpmTask) {
dependsOn npmInstall;
inputs.files('rollup.config.js');
inputs.dir('src/lib/transpiler');
outputs.files("build/js/ts.js");
// nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
// group = 'build'
args = ['run', 'build-ts'];
}
repositories {
mavenCentral();
}
dependencies {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2';
testRuntimeOnly 'org.junit.platform:junit-platform-launcher';
}
java {
sourceCompatibility = JavaVersion.VERSION_17;
targetCompatibility = JavaVersion.VERSION_17;
toolchain {
languageVersion = JavaLanguageVersion.of(17);
}
}
configure([tasks.compileJava]) {
options.release = 8;
}
jar {
manifest {
attributes(
'Main-Class': project.main_class,
'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
'Build-Branch': versioning.info.branch,
'Build-Revision': versioning.info.commit,
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
'Build-Author': 'TopchetoEU',
);
}
}
application {
mainClass = project.main_class;
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8'];
}
distZip {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude();
}
}
}
distTar {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude();
}
}
}
processResources {
dependsOn compileEnv;
dependsOn compileTypescript;
from("build/js") {
into "lib";
}
filesMatching "metadata.json", {
expand(
version: project.project_version,
name: project.project_name,
);
}
}
test {
useJUnitPlatform();
}
wrapper {
gradleVersion = '8.10';
}

View File

@@ -1,80 +0,0 @@
const { spawn } = require('child_process');
const fs = require('fs/promises');
const pt = require('path');
const { argv } = require('process');
const conf = {
name: "java-jscript",
author: "TopchetoEU",
javahome: "",
version: argv[3]
};
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
async function* find(src, dst, wildcard) {
const stat = await fs.stat(src);
if (stat.isDirectory()) {
for (const el of await fs.readdir(src)) {
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
}
}
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
}
async function copy(src, dst, wildcard) {
const promises = [];
for await (const el of find(src, dst, wildcard)) {
promises.push((async () => {
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
await fs.copyFile(el.src, el.dst);
})());
}
await Promise.all(promises);
}
function run(cmd, ...args) {
return new Promise((res, rej) => {
const proc = spawn(cmd, args, { stdio: 'inherit' });
proc.once('exit', code => {
if (code === 0) res(code);
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
});
})
}
async function compileJava() {
try {
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
.replace('${VERSION}', conf.version)
.replace('${NAME}', conf.name)
.replace('${AUTHOR}', conf.author)
);
const args = ['--release', '11', ];
if (argv[1] === 'debug') args.push('-g');
args.push('-d', 'dst/classes', 'Metadata.java');
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
await run(conf.javahome + 'javac', ...args);
}
finally {
await fs.rm('Metadata.java');
}
}
(async () => {
try {
try { await fs.rm('dst', { recursive: true }); } catch {}
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
await run('tsc', '-p', 'lib/tsconfig.json', '--outFile', 'dst/classes/me/topchetoeu/jscript/js/core.js'),
await compileJava();
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
}
catch (e) {
if (argv[2] === 'debug') throw e;
else console.log(e.toString());
}
})();

View File

@@ -1,154 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.util.Base64;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles;
import me.topchetoeu.jscript.exceptions.SyntaxException;
public class DebugServer {
public static String browserDisplayName = "jscript";
public static String targetName = "target";
public final Engine engine;
private static void send(Socket socket, String val) throws IOException {
Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes());
}
// SILENCE JAVA
private MessageDigest getDigestInstance() {
try {
return MessageDigest.getInstance("sha1");
}
catch (Throwable a) { return null; }
}
private static Thread runAsync(Runnable func, String name) {
var res = new Thread(func);
res.setName(name);
res.start();
return res;
}
private void handle(WebSocket ws) throws InterruptedException, IOException {
WebSocketMessage raw;
while ((raw = ws.receive()) != null) {
if (raw.type != Type.Text) {
ws.send(new V8Error("Expected a text message."));
continue;
}
V8Message msg;
try {
msg = new V8Message(raw.textData());
}
catch (SyntaxException e) {
ws.send(new V8Error(e.getMessage()));
return;
}
switch (msg.name) {
case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue;
case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue;
case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue;
}
}
}
private void onWsConnect(HttpRequest req, Socket socket) throws IOException {
var key = req.headers.get("sec-websocket-key");
if (key == null) {
Http.writeResponse(
socket.getOutputStream(), 426, "Upgrade Required", "text/txt",
"Expected a WS upgrade".getBytes()
);
return;
}
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
));
Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols");
Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade");
Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey);
Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket");
var ws = new WebSocket(socket);
runAsync(() -> {
try {
handle(ws);
}
catch (InterruptedException e) { return; }
catch (IOException e) { e.printStackTrace(); }
finally { ws.close(); }
}, "Debug Server Message Reader");
runAsync(() -> {
try {
handle(ws);
}
catch (InterruptedException e) { return; }
catch (IOException e) { e.printStackTrace(); }
finally { ws.close(); }
}, "Debug Server Event Writer");
}
public void open(InetSocketAddress address) throws IOException {
ServerSocket server = new ServerSocket();
server.bind(address);
try {
while (true) {
var socket = server.accept();
var req = Http.readRequest(socket.getInputStream());
switch (req.path) {
case "/json/version":
send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}");
break;
case "/json/list":
case "/json":
var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName;
send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}");
break;
case "/json/new":
case "/json/activate":
case "/json/protocol":
case "/json/close":
case "/devtools/inspector.html":
Http.writeResponse(
socket.getOutputStream(),
501, "Not Implemented", "text/txt",
"This feature isn't (and won't be) implemented.".getBytes()
);
break;
default:
if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket);
else {
Http.writeResponse(
socket.getOutputStream(),
404, "Not Found", "text/txt",
"Not found :/".getBytes()
);
}
break;
}
}
}
finally { server.close(); }
}
public DebugServer(Engine engine) {
this.engine = engine;
}
}

View File

@@ -1,52 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.BreakpointData;
import me.topchetoeu.jscript.engine.DebugCommand;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.events.Event;
public class DebugState {
private boolean paused = false;
public final HashSet<Location> breakpoints = new HashSet<>();
public final List<CodeFrame> frames = new ArrayList<>();
public final Map<String, String> sources = new HashMap<>();
public final Event<BreakpointData> breakpointNotifier = new Event<>();
public final Event<DebugCommand> commandNotifier = new Event<>();
public final Event<String> sourceAdded = new Event<>();
public DebugState pushFrame(CodeFrame frame) {
frames.add(frame);
return this;
}
public DebugState popFrame() {
if (frames.size() > 0) frames.remove(frames.size() - 1);
return this;
}
public DebugCommand pause(BreakpointData data) throws InterruptedException {
paused = true;
breakpointNotifier.next(data);
return commandNotifier.toAwaitable().await();
}
public void resume(DebugCommand command) {
paused = false;
commandNotifier.next(command);
}
// public void addSource()?
public boolean paused() { return paused; }
public boolean isBreakpoint(Location loc) {
return breakpoints.contains(loc);
}
}

View File

@@ -1,65 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.IllegalFormatException;
// We dont need no http library
public class Http {
public static void writeCode(OutputStream str, int code, String name) throws IOException {
str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes());
}
public static void writeHeader(OutputStream str, String name, String value) throws IOException {
str.write((name + ": " + value + "\r\n").getBytes());
}
public static void writeLastHeader(OutputStream str, String name, String value) throws IOException {
str.write((name + ": " + value + "\r\n").getBytes());
writeHeadersEnd(str);
}
public static void writeHeadersEnd(OutputStream str) throws IOException {
str.write("\n".getBytes());
}
public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException {
writeCode(str, code, name);
writeHeader(str, "Content-Type", type);
writeLastHeader(str, "Content-Length", data.length + "");
str.write(data);
str.close();
}
public static HttpRequest readRequest(InputStream str) throws IOException {
var lines = new BufferedReader(new InputStreamReader(str));
var line = lines.readLine();
var i1 = line.indexOf(" ");
var i2 = line.lastIndexOf(" ");
var method = line.substring(0, i1).trim().toUpperCase();
var path = line.substring(i1 + 1, i2).trim();
var headers = new HashMap<String, String>();
while (!(line = lines.readLine()).isEmpty()) {
var i = line.indexOf(":");
if (i < 0) continue;
var name = line.substring(0, i).trim().toLowerCase();
var value = line.substring(i + 1).trim();
if (name.length() == 0) continue;
headers.put(name, value);
}
if (headers.containsKey("content-length")) {
try {
var i = Integer.parseInt(headers.get("content-length"));
str.skip(i);
}
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
}
return new HttpRequest(method, path, headers);
}
}

View File

@@ -1,16 +0,0 @@
package me.topchetoeu.jscript.engine.debug;
import java.util.Map;
public class HttpRequest {
public final String method;
public final String path;
public final Map<String, String> headers;
public HttpRequest(String method, String path, Map<String, String> headers) {
this.method = method;
this.path = path;
this.headers = headers;
}
}

View File

@@ -1,29 +0,0 @@
package me.topchetoeu.jscript.engine.debug.handlers;
import java.io.IOException;
import me.topchetoeu.jscript.engine.DebugCommand;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.debug.V8Error;
import me.topchetoeu.jscript.engine.debug.V8Message;
import me.topchetoeu.jscript.engine.debug.WebSocket;
import me.topchetoeu.jscript.json.JSONMap;
public class DebuggerHandles {
public static void enable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
else ws.send(msg.respond(new JSONMap().set("debuggerId", 1)));
}
public static void disable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
if (engine.debugState == null) ws.send(msg.respond());
else ws.send(new V8Error("Debugger may not be disabled."));
}
public static void stepInto(V8Message msg, Engine engine, WebSocket ws) throws IOException {
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
else if (!engine.debugState.paused()) ws.send(new V8Error("Debugger is not paused."));
else {
engine.debugState.resume(DebugCommand.STEP_INTO);
ws.send(msg.respond());
}
}
}

View File

@@ -1,50 +0,0 @@
package me.topchetoeu.jscript.engine.modules;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import me.topchetoeu.jscript.polyfills.PolyfillEngine;
public class FileModuleProvider implements ModuleProvider {
public File root;
public final boolean allowOutside;
private boolean checkInside(Path modFile) {
return modFile.toAbsolutePath().startsWith(root.toPath().toAbsolutePath());
}
@Override
public Module getModule(File cwd, String name) {
var realName = getRealName(cwd, name);
if (realName == null) return null;
var path = Path.of(realName + ".js").normalize();
try {
var res = PolyfillEngine.streamToString(new FileInputStream(path.toFile()));
return new Module(realName, path.toString(), res);
}
catch (IOException e) {
return null;
}
}
@Override
public String getRealName(File cwd, String name) {
var path = Path.of(".", Path.of(cwd.toString(), name).normalize().toString());
var fileName = path.getFileName().toString();
if (fileName == null) return null;
if (!fileName.equals("index") && path.toFile().isDirectory()) return getRealName(cwd, name + "/index");
path = Path.of(path.toString() + ".js");
if (!allowOutside && !checkInside(path)) return null;
if (!path.toFile().isFile() || !path.toFile().canRead()) return null;
var res = path.toString().replace('\\', '/');
var i = res.lastIndexOf('.');
return res.substring(0, i);
}
public FileModuleProvider(File root, boolean allowOutside) {
this.root = root.toPath().normalize().toFile();
this.allowOutside = allowOutside;
}
}

View File

@@ -1,57 +0,0 @@
package me.topchetoeu.jscript.engine.modules;
import java.io.File;
import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.CallContext.DataKey;
import me.topchetoeu.jscript.engine.scope.Variable;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeSetter;
public class Module {
public class ExportsVariable implements Variable {
@Override
public boolean readonly() { return false; }
@Override
public Object get(CallContext ctx) { return exports; }
@Override
public void set(CallContext ctx, Object val) { exports = val; }
}
public static DataKey<Module> KEY = new DataKey<>();
public final String filename;
public final String source;
public final String name;
private Object exports = new ObjectValue();
private boolean executing = false;
@NativeGetter("name")
public String name() { return name; }
@NativeGetter("exports")
public Object exports() { return exports; }
@NativeSetter("exports")
public void setExports(Object val) { exports = val; }
public void execute(CallContext ctx) throws InterruptedException {
if (executing) return;
executing = true;
var scope = ctx.engine().global().globalChild();
scope.define(null, "module", true, this);
scope.define("exports", new ExportsVariable());
var parent = new File(filename).getParentFile();
if (parent == null) parent = new File(".");
ctx.engine().compile(scope, filename, source).call(ctx.copy().setData(KEY, this), null);
executing = false;
}
public Module(String name, String filename, String source) {
this.name = name;
this.filename = filename;
this.source = source;
}
}

View File

@@ -1,80 +0,0 @@
package me.topchetoeu.jscript.engine.modules;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.engine.CallContext;
public class ModuleManager {
private final List<ModuleProvider> providers = new ArrayList<>();
private final HashMap<String, Module> cache = new HashMap<>();
public final FileModuleProvider files;
public void addProvider(ModuleProvider provider) {
this.providers.add(provider);
}
public boolean isCached(File cwd, String name) {
name = name.replace("\\", "/");
// Absolute paths are forbidden
if (name.startsWith("/")) return false;
// Look for files if we have a relative path
if (name.startsWith("../") || name.startsWith("./")) {
var realName = files.getRealName(cwd, name);
if (cache.containsKey(realName)) return true;
else return false;
}
for (var provider : providers) {
var realName = provider.getRealName(cwd, name);
if (realName == null) continue;
if (cache.containsKey(realName)) return true;
}
return false;
}
public Module tryLoad(CallContext ctx, String name) throws InterruptedException {
name = name.replace('\\', '/');
var pcwd = Path.of(".");
if (ctx.hasData(Module.KEY)) {
pcwd = Path.of(((Module)ctx.getData(Module.KEY)).filename).getParent();
if (pcwd == null) pcwd = Path.of(".");
}
var cwd = pcwd.toFile();
if (name.startsWith("/")) return null;
if (name.startsWith("../") || name.startsWith("./")) {
var realName = files.getRealName(cwd, name);
if (realName == null) return null;
if (cache.containsKey(realName)) return cache.get(realName);
var mod = files.getModule(cwd, name);
cache.put(mod.name(), mod);
mod.execute(ctx);
return mod;
}
for (var provider : providers) {
var realName = provider.getRealName(cwd, name);
if (realName == null) continue;
if (cache.containsKey(realName)) return cache.get(realName);
var mod = provider.getModule(cwd, name);
cache.put(mod.name(), mod);
mod.execute(ctx);
return mod;
}
return null;
}
public ModuleManager(File root) {
files = new FileModuleProvider(root, false);
}
}

View File

@@ -1,9 +0,0 @@
package me.topchetoeu.jscript.engine.modules;
import java.io.File;
public interface ModuleProvider {
Module getModule(File cwd, String name);
String getRealName(File cwd, String name);
default boolean hasModule(File cwd, String name) { return getRealName(cwd, name) != null; }
}

4
gradle.properties Normal file
View File

@@ -0,0 +1,4 @@
project_group = me.topchetoeu
project_name = jscript
project_version = 0.10.0-beta
main_class = me.topchetoeu.jscript.repl.SimpleRepl

View File

@@ -1,71 +0,0 @@
interface Environment {
global: typeof globalThis & Record<string, any>;
proto(name: string): object;
setProto(name: string, val: object): void;
}
interface Internals {
markSpecial(...funcs: Function[]): void;
getEnv(func: Function): Environment | undefined;
setEnv<T>(func: T, env: Environment): T;
apply(func: Function, thisArg: any, args: any[]): any;
delay(timeout: number, callback: Function): () => void;
pushMessage(micro: boolean, func: Function, thisArg: any, args: any[]): void;
strlen(val: string): number;
char(val: string): number;
stringFromStrings(arr: string[]): string;
stringFromChars(arr: number[]): string;
symbol(name?: string): symbol;
symbolToString(sym: symbol): string;
isArray(obj: any): boolean;
generator(func: (_yield: <T>(val: T) => unknown) => (...args: any[]) => unknown): GeneratorFunction;
defineField(obj: object, key: any, val: any, writable: boolean, enumerable: boolean, configurable: boolean): boolean;
defineProp(obj: object, key: any, get: Function | undefined, set: Function | undefined, enumerable: boolean, configurable: boolean): boolean;
keys(obj: object, onlyString: boolean): any[];
ownProp(obj: any, key: string): PropertyDescriptor<any, any>;
ownPropKeys(obj: any): any[];
lock(obj: object, type: 'ext' | 'seal' | 'freeze'): void;
extensible(obj: object): boolean;
sort(arr: any[], comaprator: (a: any, b: any) => number): void;
constructor: {
log(...args: any[]): void;
}
}
var env: Environment = arguments[0], internals: Internals = arguments[1];
globalThis.log = internals.constructor.log;
try {
run('values/object');
run('values/symbol');
run('values/function');
run('values/errors');
run('values/string');
run('values/number');
run('values/boolean');
run('values/array');
run('promise');
run('map');
run('set');
run('regex');
run('timeout');
env.global.log = log;
log('Loaded polyfills!');
}
catch (e: any) {
let err = 'Uncaught error while loading polyfills: ';
if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e;
else if ('message' in e) {
if ('name' in e) err += e.name + ": " + e.message;
else err += 'Error: ' + e.message;
}
else err += e;
log(e);
}

586
lib/lib.d.ts vendored
View File

@@ -1,586 +0,0 @@
type PropertyDescriptor<T, ThisT> = {
value: any;
writable?: boolean;
enumerable?: boolean;
configurable?: boolean;
} | {
get?(this: ThisT): T;
set(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
} | {
get(this: ThisT): T;
set?(this: ThisT, val: T): void;
enumerable?: boolean;
configurable?: boolean;
};
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type Record<KeyT extends string | number | symbol, ValT> = { [x in KeyT]: ValT }
type ReplaceFunc = (match: string, ...args: any[]) => string;
type PromiseFulfillFunc<T> = (val: T) => void;
type PromiseThenFunc<T, NextT> = (val: T) => NextT;
type PromiseRejectFunc = (err: unknown) => void;
type PromiseFunc<T> = (resolve: PromiseFulfillFunc<T>, reject: PromiseRejectFunc) => void;
type PromiseResult<T> ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
// wippidy-wine, this code is now mine :D
type Awaited<T> =
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
Awaited<V> : // recursively unwrap the value
never : // the argument to `then` was not callable
T;
type IteratorYieldResult<TReturn> =
{ done?: false; } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorReturnResult<TReturn> =
{ done: true } &
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface Thenable<T> {
then<NextT>(onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
then(onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
}
interface RegExpResultIndices extends Array<[number, number]> {
groups?: { [name: string]: [number, number]; };
}
interface RegExpResult extends Array<string> {
groups?: { [name: string]: string; };
index: number;
input: string;
indices?: RegExpResultIndices;
escape(raw: string, flags: string): RegExp;
}
interface Matcher {
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
}
interface Splitter {
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
}
interface Replacer {
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
}
interface Searcher {
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
}
type FlatArray<Arr, Depth extends number> = {
"done": Arr,
"recur": Arr extends Array<infer InnerArr>
? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
: Arr
}[Depth extends -1 ? "done" : "recur"];
interface IArguments {
[i: number]: any;
length: number;
}
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>;
}
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
return?(value?: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
}
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
}
interface Generator<T = unknown, TReturn = unknown, TNext = unknown> extends Iterator<T, TReturn, TNext> {
[Symbol.iterator](): Generator<T, TReturn, TNext>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
}
interface GeneratorFunction {
new (...args: any[]): Generator;
(...args: any[]): Generator;
readonly length: number;
readonly name: string;
readonly prototype: Generator;
}
interface AsyncGenerator<T = unknown, TReturn = unknown, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw(e: any): Promise<IteratorResult<T, TReturn>>;
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}
interface AsyncGeneratorFunction {
new (...args: any[]): AsyncGenerator;
(...args: any[]): AsyncGenerator;
readonly length: number;
readonly name: string;
readonly prototype: AsyncGenerator;
}
interface MathObject {
readonly E: number;
readonly PI: number;
readonly SQRT2: number;
readonly SQRT1_2: number;
readonly LN2: number;
readonly LN10: number;
readonly LOG2E: number;
readonly LOG10E: number;
asin(x: number): number;
acos(x: number): number;
atan(x: number): number;
atan2(y: number, x: number): number;
asinh(x: number): number;
acosh(x: number): number;
atanh(x: number): number;
sin(x: number): number;
cos(x: number): number;
tan(x: number): number;
sinh(x: number): number;
cosh(x: number): number;
tanh(x: number): number;
sqrt(x: number): number;
cbrt(x: number): number;
hypot(...vals: number[]): number;
imul(a: number, b: number): number;
exp(x: number): number;
expm1(x: number): number;
pow(x: number, y: number): number;
log(x: number): number;
log10(x: number): number;
log1p(x: number): number;
log2(x: number): number;
ceil(x: number): number;
floor(x: number): number;
round(x: number): number;
fround(x: number): number;
trunc(x: number): number;
abs(x: number): number;
max(...vals: number[]): number;
min(...vals: number[]): number;
sign(x: number): number;
random(): number;
clz32(x: number): number;
}
interface Array<T> extends IterableIterator<T> {
[i: number]: T;
length: number;
toString(): string;
// toLocaleString(): string;
join(separator?: string): string;
fill(val: T, start?: number, end?: number): T[];
pop(): T | undefined;
push(...items: T[]): number;
concat(...items: (T | T[])[]): T[];
concat(...items: (T | T[])[]): T[];
join(separator?: string): string;
reverse(): T[];
shift(): T | undefined;
slice(start?: number, end?: number): T[];
sort(compareFn?: (a: T, b: T) => number): this;
splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[];
unshift(...items: T[]): number;
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
includes(el: any, start?: number): boolean;
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
flat<D extends number = 1>(depth?: D): FlatArray<T, D>;
flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray<T[], 1>;
sort(func?: (a: T, b: T) => number): this;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
entries(): IterableIterator<[number, T]>;
values(): IterableIterator<T>;
keys(): IterableIterator<number>;
}
interface ArrayConstructor {
new <T>(arrayLength?: number): T[];
new <T>(...items: T[]): T[];
<T>(arrayLength?: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is any[];
prototype: Array<any>;
}
interface Boolean {
valueOf(): boolean;
}
interface BooleanConstructor {
(val: any): boolean;
new (val: any): Boolean;
prototype: Boolean;
}
interface Error {
name: string;
message: string;
stack: string[];
toString(): string;
}
interface ErrorConstructor {
(msg?: any): Error;
new (msg?: any): Error;
prototype: Error;
}
interface TypeErrorConstructor extends ErrorConstructor {
(msg?: any): TypeError;
new (msg?: any): TypeError;
prototype: Error;
}
interface TypeError extends Error {
name: 'TypeError';
}
interface RangeErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface RangeError extends Error {
name: 'RangeError';
}
interface SyntaxErrorConstructor extends ErrorConstructor {
(msg?: any): RangeError;
new (msg?: any): RangeError;
prototype: Error;
}
interface SyntaxError extends Error {
name: 'SyntaxError';
}
interface Function {
apply(this: Function, thisArg: any, argArray?: any): any;
call(this: Function, thisArg: any, ...argArray: any[]): any;
bind(this: Function, thisArg: any, ...argArray: any[]): Function;
toString(): string;
prototype: any;
readonly length: number;
name: string;
}
interface CallableFunction extends Function {
(...args: any[]): any;
apply<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT;
call<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT;
bind<ThisArg, Args extends any[], Rest extends any[], RetT>(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT;
}
interface NewableFunction extends Function {
new(...args: any[]): any;
apply<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT;
call<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT;
bind<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT;
}
interface FunctionConstructor extends Function {
(...args: string[]): (...args: any[]) => any;
new (...args: string[]): (...args: any[]) => any;
prototype: Function;
async<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Promise<RetT>;
asyncGenerator<ArgsT extends any[], RetT>(
func: (await: <T>(val: T) => Awaited<T>, _yield: <T>(val: T) => void) => (...args: ArgsT) => RetT
): (...args: ArgsT) => AsyncGenerator<RetT>;
generator<ArgsT extends any[], T = unknown, RetT = unknown, TNext = unknown>(
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
): (...args: ArgsT) => Generator<T, RetT, TNext>;
}
interface Number {
toString(): string;
valueOf(): number;
}
interface NumberConstructor {
(val: any): number;
new (val: any): Number;
prototype: Number;
parseInt(val: unknown): number;
parseFloat(val: unknown): number;
}
interface Object {
constructor: NewableFunction;
[Symbol.typeName]: string;
valueOf(): this;
toString(): string;
hasOwnProperty(key: any): boolean;
}
interface ObjectConstructor extends Function {
(arg: string): String;
(arg: number): Number;
(arg: boolean): Boolean;
(arg?: undefined | null): {};
<T extends object>(arg: T): T;
new (arg: string): String;
new (arg: number): Number;
new (arg: boolean): Boolean;
new (arg?: undefined | null): {};
new <T extends object>(arg: T): T;
prototype: Object;
assign<T extends object>(target: T, ...src: object[]): T;
create<T extends object>(proto: T, props?: { [key: string]: PropertyDescriptor<any, T> }): T;
keys<T extends object>(obj: T, onlyString?: true): (keyof T)[];
keys<T extends object>(obj: T, onlyString: false): any[];
entries<T extends object>(obj: T, onlyString?: true): [keyof T, T[keyof T]][];
entries<T extends object>(obj: T, onlyString: false): [any, any][];
values<T extends object>(obj: T, onlyString?: true): (T[keyof T])[];
values<T extends object>(obj: T, onlyString: false): any[];
fromEntries(entries: Iterable<[any, any]>): object;
defineProperty<T, ThisT extends object>(obj: ThisT, key: any, desc: PropertyDescriptor<T, ThisT>): ThisT;
defineProperties<ThisT extends object>(obj: ThisT, desc: { [key: string]: PropertyDescriptor<any, ThisT> }): ThisT;
getOwnPropertyNames<T extends object>(obj: T): (keyof T)[];
getOwnPropertySymbols<T extends object>(obj: T): (keyof T)[];
hasOwn<T extends object, KeyT>(obj: T, key: KeyT): boolean;
getOwnPropertyDescriptor<T extends object, KeyT extends keyof T>(obj: T, key: KeyT): PropertyDescriptor<T[KeyT], T>;
getOwnPropertyDescriptors<T extends object>(obj: T): { [x in keyof T]: PropertyDescriptor<T[x], T> };
getPrototypeOf(obj: any): object | null;
setPrototypeOf<T>(obj: T, proto: object | null): T;
preventExtensions<T extends object>(obj: T): T;
seal<T extends object>(obj: T): T;
freeze<T extends object>(obj: T): T;
isExtensible(obj: object): boolean;
isSealed(obj: object): boolean;
isFrozen(obj: object): boolean;
}
interface String {
[i: number]: string;
toString(): string;
valueOf(): string;
charAt(pos: number): string;
charCodeAt(pos: number): number;
substring(start?: number, end?: number): string;
slice(start?: number, end?: number): string;
substr(start?: number, length?: number): string;
startsWith(str: string, pos?: number): string;
endsWith(str: string, pos?: number): string;
replace(pattern: string | Replacer, val: string | ReplaceFunc): string;
replaceAll(pattern: string | Replacer, val: string | ReplaceFunc): string;
match(pattern: string | Matcher): RegExpResult | string[] | null;
matchAll(pattern: string | Matcher): IterableIterator<RegExpResult>;
split(pattern: string | Splitter, limit?: number, sensible?: boolean): string;
concat(...others: string[]): string;
indexOf(term: string | Searcher, start?: number): number;
lastIndexOf(term: string | Searcher, start?: number): number;
toLowerCase(): string;
toUpperCase(): string;
trim(): string;
includes(term: string, start?: number): boolean;
length: number;
}
interface StringConstructor {
(val: any): string;
new (val: any): String;
fromCharCode(val: number): string;
prototype: String;
}
interface Symbol {
valueOf(): symbol;
}
interface SymbolConstructor {
(val?: any): symbol;
new(...args: any[]): never;
prototype: Symbol;
for(key: string): symbol;
keyFor(sym: symbol): string;
readonly typeName: unique symbol;
readonly match: unique symbol;
readonly matchAll: unique symbol;
readonly split: unique symbol;
readonly replace: unique symbol;
readonly search: unique symbol;
readonly iterator: unique symbol;
readonly asyncIterator: unique symbol;
}
interface Promise<T> extends Thenable<T> {
catch(func: PromiseRejectFunc): Promise<T>;
finally(func: () => void): Promise<T>;
}
interface PromiseConstructor {
prototype: Promise<any>;
new <T>(func: PromiseFunc<T>): Promise<Awaited<T>>;
resolve<T>(val: T): Promise<Awaited<T>>;
reject(val: any): Promise<never>;
isAwaitable(val: unknown): val is Thenable<any>;
any<T>(promises: T[]): Promise<Awaited<T>>;
race<T>(promises: (Promise<T>|T)[]): Promise<T>;
all<T extends any[]>(promises: T): Promise<{ [Key in keyof T]: Awaited<T[Key]> }>;
allSettled<T extends any[]>(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult<Awaited<T[P]>>}]>;
}
declare var String: StringConstructor;
//@ts-ignore
declare const arguments: IArguments;
declare var NaN: number;
declare var Infinity: number;
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
declare var clearTimeout: (id: number) => void;
declare var clearInterval: (id: number) => void;
declare var parseInt: typeof Number.parseInt;
declare var parseFloat: typeof Number.parseFloat;
declare function log(...vals: any[]): void;
declare var Array: ArrayConstructor;
declare var Boolean: BooleanConstructor;
declare var Promise: PromiseConstructor;
declare var Function: FunctionConstructor;
declare var Number: NumberConstructor;
declare var Object: ObjectConstructor;
declare var Symbol: SymbolConstructor;
declare var Promise: PromiseConstructor;
declare var Math: MathObject;
declare var Error: ErrorConstructor;
declare var RangeError: RangeErrorConstructor;
declare var TypeError: TypeErrorConstructor;
declare var SyntaxError: SyntaxErrorConstructor;
declare class Map<KeyT, ValueT> {
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>;
public clear(): void;
public delete(key: KeyT): boolean;
public entries(): IterableIterator<[KeyT, ValueT]>;
public keys(): IterableIterator<KeyT>;
public values(): IterableIterator<ValueT>;
public get(key: KeyT): ValueT;
public set(key: KeyT, val: ValueT): this;
public has(key: KeyT): boolean;
public get size(): number;
public forEach(func: (key: KeyT, val: ValueT, map: Map<KeyT, ValueT>) => void, thisArg?: any): void;
public constructor();
}
declare class Set<T> {
public [Symbol.iterator](): IterableIterator<T>;
public entries(): IterableIterator<[T, T]>;
public keys(): IterableIterator<T>;
public values(): IterableIterator<T>;
public clear(): void;
public add(val: T): this;
public delete(val: T): boolean;
public has(key: T): boolean;
public get size(): number;
public forEach(func: (key: T, set: Set<T>) => void, thisArg?: any): void;
public constructor();
}
declare class RegExp implements Matcher, Splitter, Replacer, Searcher {
static escape(raw: any, flags?: string): RegExp;
prototype: RegExp;
exec(val: string): RegExpResult | null;
test(val: string): boolean;
toString(): string;
[Symbol.match](target: string): RegExpResult | string[] | null;
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
readonly dotAll: boolean;
readonly global: boolean;
readonly hasIndices: boolean;
readonly ignoreCase: boolean;
readonly multiline: boolean;
readonly sticky: boolean;
readonly unicode: boolean;
readonly source: string;
readonly flags: string;
lastIndex: number;
constructor(pattern?: string, flags?: string);
constructor(pattern?: RegExp, flags?: string);
}

View File

@@ -1,93 +0,0 @@
define("map", () => {
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
const Object = env.global.Object;
class Map<KeyT, ValueT> {
[syms.values]: any = {};
public [env.global.Symbol.iterator](): IterableIterator<[KeyT, ValueT]> {
return this.entries();
}
public clear() {
this[syms.values] = {};
}
public delete(key: KeyT) {
if ((key as any) in this[syms.values]) {
delete this[syms.values];
return true;
}
else return false;
}
public entries(): IterableIterator<[KeyT, ValueT]> {
const keys = internals.ownPropKeys(this[syms.values]);
let i = 0;
return {
next: () => {
if (i >= keys.length) return { done: true };
else return { done: false, value: [ keys[i], this[syms.values][keys[i++]] ] }
},
[env.global.Symbol.iterator]() { return this; }
}
}
public keys(): IterableIterator<KeyT> {
const keys = internals.ownPropKeys(this[syms.values]);
let i = 0;
return {
next: () => {
if (i >= keys.length) return { done: true };
else return { done: false, value: keys[i] }
},
[env.global.Symbol.iterator]() { return this; }
}
}
public values(): IterableIterator<ValueT> {
const keys = internals.ownPropKeys(this[syms.values]);
let i = 0;
return {
next: () => {
if (i >= keys.length) return { done: true };
else return { done: false, value: this[syms.values][keys[i++]] }
},
[env.global.Symbol.iterator]() { return this; }
}
}
public get(key: KeyT) {
return this[syms.values][key];
}
public set(key: KeyT, val: ValueT) {
this[syms.values][key] = val;
return this;
}
public has(key: KeyT) {
return (key as any) in this[syms.values][key];
}
public get size() {
return internals.ownPropKeys(this[syms.values]).length;
}
public forEach(func: (key: KeyT, val: ValueT, map: Map<KeyT, ValueT>) => void, thisArg?: any) {
const keys = internals.ownPropKeys(this[syms.values]);
for (let i = 0; i < keys.length; i++) {
func(keys[i], this[syms.values][keys[i]], this);
}
}
public constructor(iterable: Iterable<[KeyT, ValueT]>) {
const it = iterable[env.global.Symbol.iterator]();
for (let el = it.next(); !el.done; el = it.next()) {
this[syms.values][el.value[0]] = el.value[1];
}
}
}
env.global.Map = Map;
});

View File

@@ -1,13 +0,0 @@
var { define, run } = (() => {
const modules: Record<string, Function> = {};
function define(name: string, func: Function) {
modules[name] = func;
}
function run(name: string) {
if (typeof modules[name] === 'function') return modules[name]();
else throw "The module '" + name + "' doesn't exist.";
}
return { define, run };
})();

View File

@@ -1,203 +0,0 @@
define("promise", () => {
const syms = {
callbacks: internals.symbol('Promise.callbacks'),
state: internals.symbol('Promise.state'),
value: internals.symbol('Promise.value'),
handled: internals.symbol('Promise.handled'),
} as {
readonly callbacks: unique symbol,
readonly state: unique symbol,
readonly value: unique symbol,
readonly handled: unique symbol,
}
type Callback<T> = [ PromiseFulfillFunc<T>, PromiseRejectFunc ];
enum State {
Pending,
Fulfilled,
Rejected,
}
function isAwaitable(val: unknown): val is Thenable<any> {
return (
typeof val === 'object' &&
val !== null &&
'then' in val &&
typeof val.then === 'function'
);
}
function resolve(promise: Promise<any>, v: any, state: State) {
if (promise[syms.state] === State.Pending) {
if (typeof v === 'object' && v !== null && 'then' in v && typeof v.then === 'function') {
v.then(
(res: any) => resolve(promise, res, state),
(res: any) => resolve(promise, res, State.Rejected)
);
return;
}
promise[syms.value] = v;
promise[syms.state] = state;
for (let i = 0; i < promise[syms.callbacks]!.length; i++) {
promise[syms.handled] = true;
promise[syms.callbacks]![i][state - 1](v);
}
promise[syms.callbacks] = undefined;
internals.pushMessage(true, internals.setEnv(() => {
if (!promise[syms.handled] && state === State.Rejected) {
log('Uncaught (in promise) ' + promise[syms.value]);
}
}, env), undefined, []);
}
}
class Promise<T> {
public static isAwaitable(val: unknown): val is Thenable<any> {
return isAwaitable(val);
}
public static resolve<T>(val: T): Promise<Awaited<T>> {
return new Promise(res => res(val as any));
}
public static reject<T>(val: T): Promise<Awaited<T>> {
return new Promise((_, rej) => rej(val as any));
}
public static race<T>(vals: T[]): Promise<Awaited<T>> {
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.race is not variadic.');
return new Promise((res, rej) => {
for (let i = 0; i < vals.length; i++) {
const val = vals[i];
if (this.isAwaitable(val)) val.then(res, rej);
else res(val as any);
}
});
}
public static any<T>(vals: T[]): Promise<Awaited<T>> {
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.any is not variadic.');
return new Promise((res, rej) => {
let n = 0;
for (let i = 0; i < vals.length; i++) {
const val = vals[i];
if (this.isAwaitable(val)) val.then(res, (err) => {
n++;
if (n === vals.length) throw Error('No promise resolved.');
});
else res(val as any);
}
if (vals.length === 0) throw Error('No promise resolved.');
});
}
public static all(vals: any[]): Promise<any[]> {
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.all is not variadic.');
return new Promise((res, rej) => {
const result: any[] = [];
let n = 0;
for (let i = 0; i < vals.length; i++) {
const val = vals[i];
if (this.isAwaitable(val)) val.then(
val => {
n++;
result[i] = val;
if (n === vals.length) res(result);
},
rej
);
else {
n++;
result[i] = val;
}
}
if (vals.length === n) res(result);
});
}
public static allSettled(vals: any[]): Promise<any[]> {
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.allSettled is not variadic.');
return new Promise((res, rej) => {
const result: any[] = [];
let n = 0;
for (let i = 0; i < vals.length; i++) {
const value = vals[i];
if (this.isAwaitable(value)) value.then(
value => {
n++;
result[i] = { status: 'fulfilled', value };
if (n === vals.length) res(result);
},
reason => {
n++;
result[i] = { status: 'rejected', reason };
if (n === vals.length) res(result);
},
);
else {
n++;
result[i] = { status: 'fulfilled', value };
}
}
if (vals.length === n) res(result);
});
}
[syms.callbacks]?: Callback<T>[] = [];
[syms.handled] = false;
[syms.state] = State.Pending;
[syms.value]?: T | unknown;
public then(onFulfil?: PromiseFulfillFunc<T>, onReject?: PromiseRejectFunc) {
return new Promise((resolve, reject) => {
onFulfil ??= v => v;
onReject ??= v => v;
const callback = (func: (val: any) => any) => (v: any) => {
try { resolve(func(v)); }
catch (e) { reject(e); }
}
switch (this[syms.state]) {
case State.Pending:
this[syms.callbacks]![this[syms.callbacks]!.length] = [callback(onFulfil), callback(onReject)];
break;
case State.Fulfilled:
this[syms.handled] = true;
callback(onFulfil)(this[syms.value]);
break;
case State.Rejected:
this[syms.handled] = true;
callback(onReject)(this[syms.value]);
break;
}
})
}
public catch(func: PromiseRejectFunc) {
return this.then(undefined, func);
}
public finally(func: () => void) {
return this.then(
v => {
func();
return v;
},
v => {
func();
throw v;
}
);
}
public constructor(func: PromiseFunc<T>) {
internals.pushMessage(true, func, undefined, [
((v) => resolve(this, v, State.Fulfilled)) as PromiseFulfillFunc<T>,
((err) => resolve(this, err, State.Rejected)) as PromiseRejectFunc
]);
}
}
env.global.Promise = Promise as any;
});

View File

@@ -1,143 +0,0 @@
define("regex", () => {
// var RegExp = env.global.RegExp = env.internals.RegExp;
// setProps(RegExp.prototype as RegExp, env, {
// [Symbol.typeName]: 'RegExp',
// test(val) {
// return !!this.exec(val);
// },
// toString() {
// return '/' + this.source + '/' + this.flags;
// },
// [Symbol.match](target) {
// if (this.global) {
// const res: string[] = [];
// let val;
// while (val = this.exec(target)) {
// res.push(val[0]);
// }
// this.lastIndex = 0;
// return res;
// }
// else {
// const res = this.exec(target);
// if (!this.sticky) this.lastIndex = 0;
// return res;
// }
// },
// [Symbol.matchAll](target) {
// let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
// return {
// next: (): IteratorResult<RegExpResult, undefined> => {
// const val = pattern?.exec(target);
// if (val === null || val === undefined) {
// pattern = undefined;
// return { done: true };
// }
// else return { value: val };
// },
// [Symbol.iterator]() { return this; }
// }
// },
// [Symbol.split](target, limit, sensible) {
// const pattern = new this.constructor(this, this.flags + "g") as RegExp;
// let match: RegExpResult | null;
// let lastEnd = 0;
// const res: string[] = [];
// while ((match = pattern.exec(target)) !== null) {
// let added: string[] = [];
// if (match.index >= target.length) break;
// if (match[0].length === 0) {
// added = [ target.substring(lastEnd, pattern.lastIndex), ];
// if (pattern.lastIndex < target.length) added.push(...match.slice(1));
// }
// else if (match.index - lastEnd > 0) {
// added = [ target.substring(lastEnd, match.index), ...match.slice(1) ];
// }
// else {
// for (let i = 1; i < match.length; i++) {
// res[res.length - match.length + i] = match[i];
// }
// }
// if (sensible) {
// if (limit !== undefined && res.length + added.length >= limit) break;
// else res.push(...added);
// }
// else {
// for (let i = 0; i < added.length; i++) {
// if (limit !== undefined && res.length >= limit) return res;
// else res.push(added[i]);
// }
// }
// lastEnd = pattern.lastIndex;
// }
// if (lastEnd < target.length) {
// res.push(target.substring(lastEnd));
// }
// return res;
// },
// [Symbol.replace](target, replacement) {
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
// let match: RegExpResult | null;
// let lastEnd = 0;
// const res: string[] = [];
// // log(pattern.toString());
// while ((match = pattern.exec(target)) !== null) {
// const indices = match.indices![0];
// res.push(target.substring(lastEnd, indices[0]));
// if (replacement instanceof Function) {
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
// }
// else {
// res.push(replacement);
// }
// lastEnd = indices[1];
// if (!pattern.global) break;
// }
// if (lastEnd < target.length) {
// res.push(target.substring(lastEnd));
// }
// return res.join('');
// },
// [Symbol.search](target, reverse, start) {
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
// if (!reverse) {
// pattern.lastIndex = (start as any) | 0;
// const res = pattern.exec(target);
// if (res) return res.index;
// else return -1;
// }
// else {
// start ??= target.length;
// start |= 0;
// let res: RegExpResult | null = null;
// while (true) {
// const tmp = pattern.exec(target);
// if (tmp === null || tmp.index > start) break;
// res = tmp;
// }
// if (res && res.index <= start) return res.index;
// else return -1;
// }
// },
// });
});

View File

@@ -1,81 +0,0 @@
define("set", () => {
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
const Object = env.global.Object;
class Set<T> {
[syms.values]: any = {};
public [env.global.Symbol.iterator](): IterableIterator<[T, T]> {
return this.entries();
}
public clear() {
this[syms.values] = {};
}
public delete(key: T) {
if ((key as any) in this[syms.values]) {
delete this[syms.values];
return true;
}
else return false;
}
public entries(): IterableIterator<[T, T]> {
const keys = internals.ownPropKeys(this[syms.values]);
let i = 0;
return {
next: () => {
if (i >= keys.length) return { done: true };
else return { done: false, value: [ keys[i], keys[i] ] }
},
[env.global.Symbol.iterator]() { return this; }
}
}
public keys(): IterableIterator<T> {
const keys = internals.ownPropKeys(this[syms.values]);
let i = 0;
return {
next: () => {
if (i >= keys.length) return { done: true };
else return { done: false, value: keys[i] }
},
[env.global.Symbol.iterator]() { return this; }
}
}
public values(): IterableIterator<T> {
return this.keys();
}
public add(val: T) {
this[syms.values][val] = undefined;
return this;
}
public has(key: T) {
return (key as any) in this[syms.values][key];
}
public get size() {
return internals.ownPropKeys(this[syms.values]).length;
}
public forEach(func: (key: T, val: T, map: Set<T>) => void, thisArg?: any) {
const keys = internals.ownPropKeys(this[syms.values]);
for (let i = 0; i < keys.length; i++) {
func(keys[i], this[syms.values][keys[i]], this);
}
}
public constructor(iterable: Iterable<T>) {
const it = iterable[env.global.Symbol.iterator]();
for (let el = it.next(); !el.done; el = it.next()) {
this[syms.values][el.value] = undefined;
}
}
}
env.global.Set = Set;
});

View File

@@ -1,38 +0,0 @@
define("timeout", () => {
const timeouts: Record<number, () => void> = { };
const intervals: Record<number, () => void> = { };
let timeoutI = 0, intervalI = 0;
env.global.setTimeout = (func, delay, ...args) => {
if (typeof func !== 'function') throw new TypeError("func must be a function.");
delay = (delay ?? 0) - 0;
const cancelFunc = internals.delay(delay, () => internals.apply(func, undefined, args));
timeouts[++timeoutI] = cancelFunc;
return timeoutI;
};
env.global.setInterval = (func, delay, ...args) => {
if (typeof func !== 'function') throw new TypeError("func must be a function.");
delay = (delay ?? 0) - 0;
const i = ++intervalI;
intervals[i] = internals.delay(delay, callback);
return i;
function callback() {
internals.apply(func, undefined, args);
intervals[i] = internals.delay(delay!, callback);
}
};
env.global.clearTimeout = (id) => {
const func = timeouts[id];
if (func) func();
timeouts[id] = undefined!;
};
env.global.clearInterval = (id) => {
const func = intervals[id];
if (func) func();
intervals[id] = undefined!;
};
});

View File

@@ -1,34 +0,0 @@
{
"files": [
"lib.d.ts",
"modules.ts",
"utils.ts",
"values/object.ts",
"values/symbol.ts",
"values/function.ts",
"values/errors.ts",
"values/string.ts",
"values/number.ts",
"values/boolean.ts",
"values/array.ts",
"promise.ts",
"map.ts",
"set.ts",
"regex.ts",
"timeout.ts",
"core.ts"
],
"compilerOptions": {
"outFile": "../bin/me/topchetoeu/jscript/js/core.js",
// "declarationDir": "",
// "declarationDir": "bin/me/topchetoeu/jscript/dts",
"target": "ES5",
"lib": [],
"module": "None",
"stripInternal": true,
"downlevelIteration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
}
}

View File

@@ -1,38 +0,0 @@
function setProps<
TargetT extends object,
DescT extends {
[x in Exclude<keyof TargetT, 'constructor'> ]?: TargetT[x] extends ((...args: infer ArgsT) => infer RetT) ?
((this: TargetT, ...args: ArgsT) => RetT) :
TargetT[x]
}
>(target: TargetT, desc: DescT) {
var props = internals.keys(desc, false);
for (var i = 0; i < props.length; i++) {
var key = props[i];
internals.defineField(
target, key, (desc as any)[key],
true, // writable
false, // enumerable
true // configurable
);
}
}
function setConstr(target: object, constr: Function) {
internals.defineField(
target, 'constructor', constr,
true, // writable
false, // enumerable
true // configurable
);
}
function wrapI(max: number, i: number) {
i |= 0;
if (i < 0) i = max + i;
return i;
}
function clampI(max: number, i: number) {
if (i < 0) i = 0;
if (i > max) i = max;
return i;
}

View File

@@ -1,336 +0,0 @@
define("values/array", () => {
var Array = env.global.Array = function(len?: number) {
var res = [];
if (typeof len === 'number' && arguments.length === 1) {
if (len < 0) throw 'Invalid array length.';
res.length = len;
}
else {
for (var i = 0; i < arguments.length; i++) {
res[i] = arguments[i];
}
}
return res;
} as ArrayConstructor;
env.setProto('array', Array.prototype);
(Array.prototype as any)[env.global.Symbol.typeName] = "Array";
setConstr(Array.prototype, Array);
setProps(Array.prototype, {
[env.global.Symbol.iterator]: function() {
return this.values();
},
[env.global.Symbol.typeName]: "Array",
values() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: this[i - 1] };
}
return { done: true, value: undefined };
},
[env.global.Symbol.iterator]() { return this; }
};
},
keys() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: i - 1 };
}
return { done: true, value: undefined };
},
[env.global.Symbol.iterator]() { return this; }
};
},
entries() {
var i = 0;
return {
next: () => {
while (i < this.length) {
if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] };
}
return { done: true, value: undefined };
},
[env.global.Symbol.iterator]() { return this; }
};
},
concat() {
var res = [] as any[];
res.push.apply(res, this);
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (arg instanceof Array) {
res.push.apply(res, arg);
}
else {
res.push(arg);
}
}
return res;
},
every(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
func = func.bind(thisArg);
for (var i = 0; i < this.length; i++) {
if (!func(this[i], i, this)) return false;
}
return true;
},
some(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
func = func.bind(thisArg);
for (var i = 0; i < this.length; i++) {
if (func(this[i], i, this)) return true;
}
return false;
},
fill(val, start, end) {
if (arguments.length < 3) end = this.length;
if (arguments.length < 2) start = 0;
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
for (; start < end; start++) {
this[start] = val;
}
return this;
},
filter(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
var res = [];
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]);
}
return res;
},
find(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
}
return undefined;
},
findIndex(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = 0; i < this.length; i++) {
if (i in this && func.call(thisArg, this[i], i, this)) return i;
}
return -1;
},
findLast(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = this.length - 1; i >= 0; i--) {
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
}
return undefined;
},
findLastIndex(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
for (var i = this.length - 1; i >= 0; i--) {
if (i in this && func.call(thisArg, this[i], i, this)) return i;
}
return -1;
},
flat(depth) {
var res = [] as any[];
var buff = [];
res.push(...this);
for (var i = 0; i < (depth ?? 1); i++) {
var anyArrays = false;
for (var el of res) {
if (el instanceof Array) {
buff.push(...el);
anyArrays = true;
}
else buff.push(el);
}
res = buff;
buff = [];
if (!anyArrays) break;
}
return res;
},
flatMap(func, th) {
return this.map(func, th).flat();
},
forEach(func, thisArg) {
for (var i = 0; i < this.length; i++) {
if (i in this) func.call(thisArg, this[i], i, this);
}
},
map(func, thisArg) {
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
var res = [];
for (var i = 0; i < this.length; i++) {
if (i in this) res[i] = func.call(thisArg, this[i], i, this);
}
return res;
},
pop() {
if (this.length === 0) return undefined;
var val = this[this.length - 1];
this.length--;
return val;
},
push() {
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return arguments.length;
},
shift() {
if (this.length === 0) return undefined;
var res = this[0];
for (var i = 0; i < this.length - 1; i++) {
this[i] = this[i + 1];
}
this.length--;
return res;
},
unshift() {
for (var i = this.length - 1; i >= 0; i--) {
this[i + arguments.length] = this[i];
}
for (var i = 0; i < arguments.length; i++) {
this[i] = arguments[i];
}
return arguments.length;
},
slice(start, end) {
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
var res: any[] = [];
var n = end - start;
if (n <= 0) return res;
for (var i = 0; i < n; i++) {
res[i] = this[start + i];
}
return res;
},
toString() {
let res = '';
for (let i = 0; i < this.length; i++) {
if (i > 0) res += ',';
if (i in this && this[i] !== undefined && this[i] !== null) res += this[i];
}
return res;
},
indexOf(el, start) {
start = start! | 0;
for (var i = Math.max(0, start); i < this.length; i++) {
if (i in this && this[i] == el) return i;
}
return -1;
},
lastIndexOf(el, start) {
start = start! | 0;
for (var i = this.length; i >= start; i--) {
if (i in this && this[i] == el) return i;
}
return -1;
},
includes(el, start) {
return this.indexOf(el, start) >= 0;
},
join(val = ',') {
let res = '', first = true;
for (let i = 0; i < this.length; i++) {
if (!(i in this)) continue;
if (!first) res += val;
first = false;
res += this[i];
}
return res;
},
sort(func) {
func ??= (a, b) => {
const _a = a + '';
const _b = b + '';
if (_a > _b) return 1;
if (_a < _b) return -1;
return 0;
};
if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.');
internals.sort(this, func);
return this;
},
splice(start, deleteCount, ...items) {
start = clampI(this.length, wrapI(this.length, start ?? 0));
deleteCount = (deleteCount ?? Infinity | 0);
if (start + deleteCount >= this.length) deleteCount = this.length - start;
const res = this.slice(start, start + deleteCount);
const moveN = items.length - deleteCount;
const len = this.length;
if (moveN < 0) {
for (let i = start - moveN; i < len; i++) {
this[i + moveN] = this[i];
}
}
else if (moveN > 0) {
for (let i = len - 1; i >= start; i--) {
this[i + moveN] = this[i];
}
}
for (let i = 0; i < items.length; i++) {
this[i + start] = items[i];
}
this.length = len + moveN;
return res;
}
});
setProps(Array, {
isArray(val: any) { return internals.isArray(val); }
});
internals.markSpecial(Array);
});

View File

@@ -1,12 +0,0 @@
define("values/boolean", () => {
var Boolean = env.global.Boolean = function (this: Boolean | undefined, arg) {
var val;
if (arguments.length === 0) val = false;
else val = !!arg;
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as BooleanConstructor;
env.setProto('bool', Boolean.prototype);
setConstr(Boolean.prototype, Boolean);
});

View File

@@ -1,46 +0,0 @@
define("values/errors", () => {
var Error = env.global.Error = function Error(msg: string) {
if (msg === undefined) msg = '';
else msg += '';
return Object.setPrototypeOf({
message: msg,
stack: [] as string[],
}, Error.prototype);
} as ErrorConstructor;
setConstr(Error.prototype, Error);
setProps(Error.prototype, {
name: 'Error',
toString: internals.setEnv(function(this: Error) {
if (!(this instanceof Error)) return '';
if (this.message === '') return this.name;
else return this.name + ': ' + this.message;
}, env)
});
env.setProto('error', Error.prototype);
internals.markSpecial(Error);
function makeError<T1 extends ErrorConstructor>(name: string, proto: string): T1 {
function constr (msg: string) {
var res = new Error(msg);
(res as any).__proto__ = constr.prototype;
return res;
}
(constr as any).__proto__ = Error;
(constr.prototype as any).__proto__ = env.proto('error');
setConstr(constr.prototype, constr as ErrorConstructor);
setProps(constr.prototype, { name: name });
internals.markSpecial(constr);
env.setProto(proto, constr.prototype);
return constr as T1;
}
env.global.RangeError = makeError('RangeError', 'rangeErr');
env.global.TypeError = makeError('TypeError', 'typeErr');
env.global.SyntaxError = makeError('SyntaxError', 'syntaxErr');
});

View File

@@ -1,140 +0,0 @@
define("values/function", () => {
var Function = env.global.Function = function() {
throw 'Using the constructor Function() is forbidden.';
} as unknown as FunctionConstructor;
env.setProto('function', Function.prototype);
setConstr(Function.prototype, Function);
setProps(Function.prototype, {
apply(thisArg, args) {
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
var len = args.length - 0;
let newArgs: any[];
if (internals.isArray(args)) newArgs = args;
else {
newArgs = [];
while (len >= 0) {
len--;
newArgs[len] = args[len];
}
}
return internals.apply(this, thisArg, newArgs);
},
call(thisArg, ...args) {
return this.apply(thisArg, args);
},
bind(thisArg, ...args) {
const func = this;
const res = function() {
const resArgs = [];
for (let i = 0; i < args.length; i++) {
resArgs[i] = args[i];
}
for (let i = 0; i < arguments.length; i++) {
resArgs[i + args.length] = arguments[i];
}
return func.apply(thisArg, resArgs);
};
res.name = "<bound> " + func.name;
return res;
},
toString() {
return 'function (...) { ... }';
},
});
setProps(Function, {
async(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
return function (this: any) {
const args = arguments;
return new Promise((res, rej) => {
const gen = internals.apply(internals.generator(func as any), this, args as any);
(function next(type: 'none' | 'err' | 'ret', val?: any) {
try {
let result;
switch (type) {
case 'err': result = gen.throw(val); break;
case 'ret': result = gen.next(val); break;
case 'none': result = gen.next(); break;
}
if (result.done) res(result.value);
else Promise.resolve(result.value).then(
v => next('ret', v),
v => next('err', v)
)
}
catch (e) {
rej(e);
}
})('none');
});
};
},
asyncGenerator(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
return function(this: any, ...args: any[]) {
const gen = internals.apply(internals.generator((_yield) => func(
val => _yield(['await', val]) as any,
val => _yield(['yield', val])
)), this, args) as Generator<['await' | 'yield', any]>;
const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => {
let res;
try {
switch (type) {
case 'val': res = gen.next(val); break;
case 'ret': res = gen.return(val); break;
case 'err': res = gen.throw(val); break;
default: res = gen.next(); break;
}
}
catch (e) { return reject(e); }
if (res.done) return { done: true, res: <any>res };
else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then(
v => next(resolve, reject, 'val', v),
v => next(resolve, reject, 'err', v),
)
else resolve({ done: false, value: res.value[1] });
};
return {
next() {
const args = arguments;
if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none'));
else return new Promise((res, rej) => next(res, rej, 'val', args[0]));
},
return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)),
throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)),
[env.global.Symbol.asyncIterator]() { return this; }
}
}
},
generator(func) {
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
const gen = internals.generator(func);
return function(this: any, ...args: any[]) {
const it = internals.apply(gen, this, args);
return {
next: (...args) => internals.apply(it.next, it, args),
return: (val) => internals.apply(it.next, it, [val]),
throw: (val) => internals.apply(it.next, it, [val]),
[env.global.Symbol.iterator]() { return this; }
}
}
}
})
internals.markSpecial(Function);
});

View File

@@ -1,33 +0,0 @@
define("values/number", () => {
var Number = env.global.Number = function(this: Number | undefined, arg: any) {
var val;
if (arguments.length === 0) val = 0;
else val = arg - 0;
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as NumberConstructor;
env.setProto('number', Number.prototype);
setConstr(Number.prototype, Number);
setProps(Number.prototype, {
valueOf() {
if (typeof this === 'number') return this;
else return (this as any).value;
},
toString() {
if (typeof this === 'number') return this + '';
else return (this as any).value + '';
}
});
setProps(Number, {
parseInt(val) { return Math.trunc(val as any - 0); },
parseFloat(val) { return val as any - 0; },
});
env.global.parseInt = Number.parseInt;
env.global.parseFloat = Number.parseFloat;
env.global.Object.defineProperty(env.global, 'NaN', { value: 0 / 0, writable: false });
env.global.Object.defineProperty(env.global, 'Infinity', { value: 1 / 0, writable: false });
});

View File

@@ -1,226 +0,0 @@
define("values/object", () => {
var Object = env.global.Object = function(arg: any) {
if (arg === undefined || arg === null) return {};
else if (typeof arg === 'boolean') return new Boolean(arg);
else if (typeof arg === 'number') return new Number(arg);
else if (typeof arg === 'string') return new String(arg);
return arg;
} as ObjectConstructor;
env.setProto('object', Object.prototype);
(Object.prototype as any).__proto__ = null;
setConstr(Object.prototype, Object as any);
function throwNotObject(obj: any, name: string) {
if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') {
throw new TypeError(`Object.${name} may only be used for objects.`);
}
}
function check(obj: any) {
return typeof obj === 'object' && obj !== null || typeof obj === 'function';
}
setProps(Object, {
assign(dst, ...src) {
throwNotObject(dst, 'assign');
for (let i = 0; i < src.length; i++) {
const obj = src[i];
throwNotObject(obj, 'assign');
for (const key of Object.keys(obj)) {
(dst as any)[key] = (obj as any)[key];
}
}
return dst;
},
create(obj, props) {
props ??= {};
return Object.defineProperties({ __proto__: obj }, props as any) as any;
},
defineProperty(obj, key, attrib) {
throwNotObject(obj, 'defineProperty');
if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.');
if ('value' in attrib) {
if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.');
if (!internals.defineField(
obj, key,
attrib.value,
!!attrib.writable,
!!attrib.enumerable,
!!attrib.configurable
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
}
else {
if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.');
if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.');
if (!internals.defineProp(
obj, key,
attrib.get,
attrib.set,
!!attrib.enumerable,
!!attrib.configurable
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
}
return obj;
},
defineProperties(obj, attrib) {
throwNotObject(obj, 'defineProperties');
if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.';
for (var key in attrib) {
Object.defineProperty(obj, key, attrib[key]);
}
return obj;
},
keys(obj, onlyString) {
return internals.keys(obj, !!(onlyString ?? true));
},
entries(obj, onlyString) {
const res = [];
const keys = internals.keys(obj, !!(onlyString ?? true));
for (let i = 0; i < keys.length; i++) {
res[i] = [ keys[i], (obj as any)[keys[i]] ];
}
return keys;
},
values(obj, onlyString) {
const res = [];
const keys = internals.keys(obj, !!(onlyString ?? true));
for (let i = 0; i < keys.length; i++) {
res[i] = (obj as any)[keys[i]];
}
return keys;
},
getOwnPropertyDescriptor(obj, key) {
return internals.ownProp(obj, key) as any;
},
getOwnPropertyDescriptors(obj) {
const res = [];
const keys = internals.ownPropKeys(obj);
for (let i = 0; i < keys.length; i++) {
res[i] = internals.ownProp(obj, keys[i]);
}
return res;
},
getOwnPropertyNames(obj) {
const arr = internals.ownPropKeys(obj);
const res = [];
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'symbol') continue;
res[res.length] = arr[i];
}
return res as any;
},
getOwnPropertySymbols(obj) {
const arr = internals.ownPropKeys(obj);
const res = [];
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'symbol') continue;
res[res.length] = arr[i];
}
return res as any;
},
hasOwn(obj, key) {
const keys = internals.ownPropKeys(obj);
for (let i = 0; i < keys.length; i++) {
if (keys[i] === key) return true;
}
return false;
},
getPrototypeOf(obj) {
return obj.__proto__;
},
setPrototypeOf(obj, proto) {
(obj as any).__proto__ = proto;
return obj;
},
fromEntries(iterable) {
const res = {} as any;
for (const el of iterable) {
res[el[0]] = el[1];
}
return res;
},
preventExtensions(obj) {
throwNotObject(obj, 'preventExtensions');
internals.lock(obj, 'ext');
return obj;
},
seal(obj) {
throwNotObject(obj, 'seal');
internals.lock(obj, 'seal');
return obj;
},
freeze(obj) {
throwNotObject(obj, 'freeze');
internals.lock(obj, 'freeze');
return obj;
},
isExtensible(obj) {
if (!check(obj)) return false;
return internals.extensible(obj);
},
isSealed(obj) {
if (!check(obj)) return true;
if (internals.extensible(obj)) return false;
const keys = internals.ownPropKeys(obj);
for (let i = 0; i < keys.length; i++) {
if (internals.ownProp(obj, keys[i]).configurable) return false;
}
return true;
},
isFrozen(obj) {
if (!check(obj)) return true;
if (internals.extensible(obj)) return false;
const keys = internals.ownPropKeys(obj);
for (let i = 0; i < keys.length; i++) {
const prop = internals.ownProp(obj, keys[i]);
if (prop.configurable) return false;
if ('writable' in prop && prop.writable) return false;
}
return true;
}
});
setProps(Object.prototype, {
valueOf() {
return this;
},
toString() {
return '[object ' + (this[env.global.Symbol.typeName] ?? 'Unknown') + ']';
},
hasOwnProperty(key) {
return Object.hasOwn(this, key);
},
});
internals.markSpecial(Object);
});

View File

@@ -1,267 +0,0 @@
define("values/string", () => {
var String = env.global.String = function(this: String | undefined, arg: any) {
var val;
if (arguments.length === 0) val = '';
else val = arg + '';
if (this === undefined || this === null) return val;
else (this as any).value = val;
} as StringConstructor;
env.setProto('string', String.prototype);
setConstr(String.prototype, String);
setProps(String.prototype, {
toString() {
if (typeof this === 'string') return this;
else return (this as any).value;
},
valueOf() {
if (typeof this === 'string') return this;
else return (this as any).value;
},
substring(start, end) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.substring(start, end);
else throw new Error('This function may be used only with primitive or object strings.');
}
start = start ?? 0 | 0;
end = (end ?? this.length) | 0;
const res = [];
for (let i = start; i < end; i++) {
if (i >= 0 && i < this.length) res[res.length] = this[i];
}
return internals.stringFromStrings(res);
},
substr(start, length) {
start = start ?? 0 | 0;
if (start >= this.length) start = this.length - 1;
if (start < 0) start = 0;
length = (length ?? this.length - start) | 0;
const end = length + start;
const res = [];
for (let i = start; i < end; i++) {
if (i >= 0 && i < this.length) res[res.length] = this[i];
}
return internals.stringFromStrings(res);
},
toLowerCase() {
// TODO: Implement localization
const res = [];
for (let i = 0; i < this.length; i++) {
const c = internals.char(this[i]);
if (c >= 65 && c <= 90) res[i] = c - 65 + 97;
else res[i] = c;
}
return internals.stringFromChars(res);
},
toUpperCase() {
// TODO: Implement localization
const res = [];
for (let i = 0; i < this.length; i++) {
const c = internals.char(this[i]);
if (c >= 97 && c <= 122) res[i] = c - 97 + 65;
else res[i] = c;
}
return internals.stringFromChars(res);
},
charAt(pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.charAt(pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = pos | 0;
if (pos < 0 || pos >= this.length) return '';
return this[pos];
},
charCodeAt(pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.charAt(pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = pos | 0;
if (pos < 0 || pos >= this.length) return 0 / 0;
return internals.char(this[pos]);
},
startsWith(term, pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.startsWith(term, pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = pos! | 0;
term = term + "";
if (pos < 0 || this.length < term.length + pos) return false;
for (let i = 0; i < term.length; i++) {
if (this[i + pos] !== term[i]) return false;
}
return true;
},
endsWith(term, pos) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.endsWith(term, pos);
else throw new Error('This function may be used only with primitive or object strings.');
}
pos = (pos ?? this.length) | 0;
term = term + "";
const start = pos - term.length;
if (start < 0 || this.length < term.length + start) return false;
for (let i = 0; i < term.length; i++) {
if (this[i + start] !== term[i]) return false;
}
return true;
},
indexOf(term: any, start) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.indexOf(term, start);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
return term[env.global.Symbol.search](this, false, start);
},
lastIndexOf(term: any, start) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.indexOf(term, start);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
return term[env.global.Symbol.search](this, true, start);
},
includes(term, start) {
return this.indexOf(term, start) >= 0;
},
replace(pattern: any, val) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.replace(pattern, val);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
return pattern[env.global.Symbol.replace](this, val);
},
replaceAll(pattern: any, val) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.replace(pattern, val);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g");
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
return pattern[env.global.Symbol.replace](this, val);
},
match(pattern: any) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.match(pattern);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
return pattern[env.global.Symbol.match](this);
},
matchAll(pattern: any) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.matchAll(pattern);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g");
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
return pattern[env.global.Symbol.match](this);
},
split(pattern: any, lim, sensible) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.split(pattern, lim, sensible);
else throw new Error('This function may be used only with primitive or object strings.');
}
if (typeof pattern[env.global.Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
return pattern[env.global.Symbol.split](this, lim, sensible);
},
slice(start, end) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.slice(start, end);
else throw new Error('This function may be used only with primitive or object strings.');
}
start = wrapI(this.length, start ?? 0 | 0);
end = wrapI(this.length, end ?? this.length | 0);
if (start > end) return '';
return this.substring(start, end);
},
concat(...args) {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.concat(...args);
else throw new Error('This function may be used only with primitive or object strings.');
}
var res = this;
for (var arg of args) res += arg;
return res;
},
trim() {
return this
.replace(/^\s+/g, '')
.replace(/\s+$/g, '');
}
});
setProps(String, {
fromCharCode(val) {
return internals.stringFromChars([val | 0]);
},
})
env.global.Object.defineProperty(String.prototype, 'length', {
get() {
if (typeof this !== 'string') {
if (this instanceof String) return (this as any).value.length;
else throw new Error('This function may be used only with primitive or object strings.');
}
return internals.strlen(this);
},
configurable: true,
enumerable: false,
});
});

View File

@@ -1,36 +0,0 @@
define("values/symbol", () => {
const symbols: Record<string, symbol> = { };
var Symbol = env.global.Symbol = function(this: any, val?: string) {
if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'.");
if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.');
return internals.symbol(val);
} as SymbolConstructor;
env.setProto('symbol', Symbol.prototype);
setConstr(Symbol.prototype, Symbol);
setProps(Symbol, {
for(key) {
if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.');
if (key in symbols) return symbols[key];
else return symbols[key] = internals.symbol(key);
},
keyFor(sym) {
if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.');
return internals.symbolToString(sym);
},
typeName: Symbol("Symbol.name") as any,
replace: Symbol('Symbol.replace') as any,
match: Symbol('Symbol.match') as any,
matchAll: Symbol('Symbol.matchAll') as any,
split: Symbol('Symbol.split') as any,
search: Symbol('Symbol.search') as any,
iterator: Symbol('Symbol.iterator') as any,
asyncIterator: Symbol('Symbol.asyncIterator') as any,
});
internals.defineField(env.global.Object.prototype, Symbol.typeName, 'Object', false, false, false);
internals.defineField(env.global, Symbol.typeName, 'Window', false, false, false);
});

6
package-lock.json generated
View File

@@ -1,6 +0,0 @@
{
"name": "java-jscript",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@@ -1 +1,31 @@
{}
{
"scripts": {
"build-env": "rollup -c --environment INPUT:src/lib/libs/_entry.ts,OUTPUT:build/js/index.js,POLYFILLS:src/lib/libs/polyfills",
"build-ts": "rollup -c --environment INPUT:src/lib/transpiler/_entry.ts,OUTPUT:build/js/ts.js"
},
"dependencies": {
"@babel/core": "^7.26.0",
"@babel/runtime": "^7.26.0",
"@babel/standalone": "^7.26.4",
"@rollup/plugin-json": "^6.1.0",
"@types/babel__preset-env": "^7.9.7",
"@types/babel__standalone": "^7.1.9",
"@types/coffeescript": "^2.5.7",
"coffeescript": "^2.7.0",
"typescript": "^5.7.2"
},
"devDependencies": {
"@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/plugin-transform-typescript": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@types/node": "^22.10.1",
"rollup": "^4.24.0",
"tslib": "^2.8.0"
}
}

130
rollup.config.js Normal file
View File

@@ -0,0 +1,130 @@
const { defineConfig } = require("rollup");
const terser = require("@rollup/plugin-terser");
const typescript = require("@rollup/plugin-typescript");
const babel = require("@rollup/plugin-babel");
const commonjs = require("@rollup/plugin-commonjs");
const nodeResolve = require("@rollup/plugin-node-resolve");
const json = require("@rollup/plugin-json");
const { resolve } = require("path");
const shouldMinify = () => false;
const shouldEmitSourcemaps = () => true;
const shouldPolyfill = () => !!process.env.POLYFILLS;
const construct = (input, output) => defineConfig({
input,
plugins: [
shouldPolyfill() && {
name: "babel-fake-runtime",
resolveId(source) {
if (source.startsWith("!polyfills:/helpers")) return resolve(process.env.POLYFILLS) + source.slice(19) + ".js";
}
},
commonjs(),
nodeResolve(),
json(),
babel({
extensions: [],
exclude: ["node_modules/**"],
babelHelpers: "runtime",
plugins: [
["@babel/plugin-transform-typescript", {
onlyRemoveTypeImports: true,
optimizeConstEnums: true,
allowDeclareFields: true,
}],
["@babel/plugin-transform-class-properties"],
["@babel/plugin-transform-runtime", {
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
version: "^7.24.0",
}],
]
}),
babel({
extensions: [],
exclude: shouldPolyfill() ? [process.env.POLYFILLS + "/**"] : [],
assumptions: {
ignoreToPrimitiveHint: true,
noClassCalls: true,
},
env: {
development: { compact: false },
},
babelHelpers: "runtime",
plugins: [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-classes",
"@babel/plugin-transform-computed-properties",
"@babel/plugin-transform-destructuring",
"@babel/plugin-transform-for-of",
"@babel/plugin-transform-object-super",
"@babel/plugin-transform-parameters",
"@babel/plugin-transform-shorthand-properties",
"@babel/plugin-transform-spread",
"@babel/plugin-transform-object-rest-spread",
"@babel/plugin-transform-template-literals",
"@babel/plugin-transform-unicode-escapes",
"@babel/plugin-transform-unicode-regex",
"@babel/plugin-transform-exponentiation-operator",
"@babel/plugin-transform-async-to-generator",
"@babel/plugin-transform-async-generator-functions",
"@babel/plugin-transform-nullish-coalescing-operator",
"@babel/plugin-transform-optional-chaining",
"@babel/plugin-transform-logical-assignment-operators",
"@babel/plugin-transform-numeric-separator",
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-class-static-block",
"@babel/plugin-transform-regenerator",
["@babel/plugin-transform-runtime", {
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
version: "^7.24.0",
}],
],
}),
typescript({
exclude: ["node_modules/**", "*.js"],
compilerOptions: {
allowImportingTsExtensions: true,
noEmit: true,
},
noForceEmit: true,
noEmitOnError: true,
}),
shouldMinify() && terser({
sourceMap: shouldEmitSourcemaps(),
keep_classnames: true,
}),
],
output: {
file: output,
format: "iife",
globals: {
fs: "null",
path: "null",
os: "null",
inspector: "null",
tty: "null",
util: "null",
assert: "null",
url: "null",
"@babel/preset-typescript/package.json": "null",
module: "null",
process: "null",
v8: "null",
},
// plugins: [babel.getBabelOutputPlugin({
// allowAllFormats: true,
// })],
sourcemap: shouldEmitSourcemaps(),
inlineDynamicImports: true,
},
});
module.exports = construct(process.env.INPUT, process.env.OUTPUT);

12
settings.gradle Normal file
View File

@@ -0,0 +1,12 @@
pluginManagement {
repositories {
mavenCentral();
gradlePluginPortal();
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0';
}
rootProject.name = properties.project_name;

91
src/lib/libs/_entry.ts Normal file
View File

@@ -0,0 +1,91 @@
import { object, setGlobalPrototypes, target } from "./primordials.ts";
import { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts";
import { Boolean } from "./values/boolean.ts";
import { Function } from "./values/function.ts";
import { Number } from "./values/number.ts";
import { Object } from "./values/object.ts";
import { String } from "./values/string.ts";
import { Symbol } from "./values/symbol.ts";
import { Array } from "./values/array.ts";
import { Map, WeakMap } from "./classes/map.ts";
import { RegExp } from "./values/regex.ts";
import { Date } from "./classes/date.ts";
import { Math as _Math } from "./namespaces/math.ts";
import { Set, WeakSet } from "./classes/set.ts";
import { JSON } from "./namespaces/json.ts";
import { console } from "./namespaces/console.ts";
import { encodeURI, encodeURIComponent } from "./url.ts";
import { Promise } from "./classes/promise.ts";
import { ArrayBuffer } from "./arrays/ArrayBuffer.ts";
import { Uint8Array } from "./arrays/Uint8Array.ts";
import { Int32Array } from "./arrays/Int32Array.ts";
import { TypedArray } from "./arrays/TypedArray.ts";
declare global {
function print(...args: any[]): void;
function measure(func: Function): void;
}
function fixup<T extends Function>(clazz: T) {
object.setPrototype(clazz, Function.prototype);
object.setPrototype(clazz.prototype as any, Object.prototype);
return clazz;
}
object.setPrototype(target, Object.prototype);
object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 });
target.Symbol = fixup(Symbol);
target.Number = fixup(Number);
target.String = fixup(String);
target.Boolean = fixup(Boolean);
target.Object = Object;
target.Function = fixup(Function);
target.Array = fixup(Array);
target.Error = fixup(Error);
target.RangeError = RangeError;
target.SyntaxError = SyntaxError;
target.TypeError = TypeError;
fixup(TypedArray);
target.ArrayBuffer = fixup(ArrayBuffer);
target.Uint8Array = Uint8Array;
target.Int32Array = Int32Array;
target.Map = fixup(Map);
target.WeakMap = fixup(WeakMap);
target.Set = fixup(Set);
target.WeakSet = fixup(WeakSet);
target.RegExp = fixup(RegExp);
target.Date = fixup(Date);
target.Promise = fixup(Promise);
target.Math = object.setPrototype(_Math, Object.prototype);
target.JSON = object.setPrototype(JSON, Object.prototype);
target.console = object.setPrototype(console, Object.prototype);
target.parseInt = Number.parseInt;
target.parseFloat = Number.parseFloat;
target.NaN = Number.NaN;
target.Infinity = Number.POSITIVE_INFINITY;
target.encodeURI = encodeURI;
target.encodeURIComponent = encodeURIComponent;
setGlobalPrototypes({
string: String.prototype,
number: Number.prototype,
boolean: Boolean.prototype,
symbol: Symbol.prototype,
object: Object.prototype,
array: Array.prototype,
function: Function.prototype,
error: Error.prototype,
syntax: SyntaxError.prototype,
range: RangeError.prototype,
type: TypeError.prototype,
uint8: Uint8Array.prototype,
int32: Int32Array.prototype,
regex: RegExp,
});

View File

@@ -0,0 +1,30 @@
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
export const abs = new map(true);
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
export class ArrayBuffer {
public [abKey]!: InternalBuffer;
public get byteLength() {
return this[abKey].length;
}
public get byteOffset() {
return 0;
}
public constructor(val: unknown) {
if (buffer.isBuff(val)) this[abKey] = val;
else this[abKey] = buffer.buff(Number(val));
}
}
export function getAB(buff: InternalBuffer): ArrayBuffer {
let res = abs.get(buff);
if (res == null) {
res = new ArrayBuffer(buff);
abs.set(buff, res);
}
return res;
}

View File

@@ -0,0 +1,25 @@
import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.int32;
const funcs = typedArrayFuncs(4, factory);
export class Int32Array extends TypedArray {
public subarray(this: number[], start?: number, end?: number) {
return funcs.subarray(this, start, end);
}
public slice(this: any[], start?: number, end?: number) {
return funcs.slice(this, start, end);
}
public map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
return funcs.map(this, cb, self);
}
public filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
return funcs.filter(this, cb, self);
}
public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore);
return funcs.construct(obj, start, end) as any;
}
}

View File

@@ -0,0 +1,220 @@
import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts";
import { symbols, wrapI } from "../utils.ts";
import { Error, TypeError } from "../values/errors.ts";
import { abKey, ArrayBuffer, getAB } from "./ArrayBuffer.ts";
export const abstractIgnore = symbol.getSymbol("TypedArray.abstractIgnore");
export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
return {
map(self: number[], cb: (val: number, i: number, self: number[]) => number, fnSelf: any) {
const res = constructor(buffer.buff(self.length * perEl), 0, self.length);
for (let i = 0; i < self.length; i++) {
res[i] = func.invoke(cb, fnSelf, [self[i], i, self]);
}
return res;
},
filter(self: number[], cb: (val: number, i: number, self: number[]) => boolean, fnSelf: any) {
const bigger = constructor(buffer.buff(self.length * perEl), 0, self.length);
let j = 0;
for (let i = 0; i < self.length; i++) {
if (func.invoke(cb, self, [self[i], i, fnSelf])) bigger[j++] = self[i];
}
const res = constructor(buffer.buff(j * perEl), 0, j);
for (let i = 0; i < j; i++) res[i] = bigger[i];
return res;
},
slice(self: number[], start = 0, end = self.length) {
start = wrapI(start, self.length);
end = wrapI(end, self.length);
if (end <= start) return constructor(buffer.buff(0), 0, 0);
const res = constructor(buffer.buff((end - start) * perEl), 0, end - start);
for (let i = 0; i < end - start; i++) {
res[i] = self[start + i];
}
return res;
},
subarray(self: number[], start = 0, end = self.length) {
start = wrapI(start, self.length);
end = wrapI(end, self.length);
if (end <= start) return constructor(buffer.buff(0), 0, 0);
const offset = buffer.start(self);
return constructor(buffer.backer(self), offset + start, offset + end);
},
construct(self: ArrayBuffer | number[] | number | Iterable<number>, start?: number, end?: number) {
if (typeof self === "number") {
return constructor(buffer.buff(self * perEl), 0, self);
}
if (self instanceof ArrayBuffer) {
const internal = self[abKey];
if (start === undefined) start = 0;
if (end === undefined) end = (internal.length / perEl) | 0;
return constructor(internal, start, end);
}
if (symbols.iterator in self && typeof self[symbols.iterator] === "function") {
const arr: number[] = [];
let i = 0;
const gen: Iterator<number> = self[symbols.iterator]();
for (let it = gen.next(); !it.done; it = gen.next()) {
arr[i++] = Number(it.value);
}
const res = constructor(buffer.buff(i * perEl), 0, i);
for (let j = 0; j < i; j++) res[j] = arr[j];
return res;
}
const res = constructor(buffer.buff((self as number[]).length * perEl), 0, (self as number[]).length);
for (let i = 0; i < (self as number[]).length; i++) res[i] = (self as number[])[i];
return res;
},
byteOffset(self: number[]) {
return buffer.start(self) * perEl;
},
byteLength(self: number[]) {
return (buffer.end(self) - buffer.start(self)) * perEl;
},
};
}
export class TypedArray {
public get buffer() {
return getAB(buffer.backer(this as any));
}
public get byteOffset(): number {
throw new Error("abstract");
}
public get byteLength(): number {
throw new Error("abstract");
}
public forEach(this: number[], cb: (val: number, i: number, self: this) => void, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this) func.invoke(cb, self, [this[i], i, this]);
}
}
public join(this: number[], delim = ",") {
delim = String(delim);
const parts = [];
if (delim) {
for (let i = 0; i < this.length; i++) {
if (i) parts[parts.length] = delim;
parts[parts.length] = (i in this) ? String(this[i]) : "";
}
}
else {
for (let i = 0; i < this.length; i++) {
parts[i] = (i in this) ? String(this[i]) : "";
}
}
return string.stringBuild(parts);
}
public subarray(this: number[], start = 0, end = this.length) {
throw new Error("'slice' is an abstract method");
}
public slice(this: number[], start = 0, end = this.length) {
throw new Error("'slice' is an abstract method");
}
public map(this: number[], cb: (val: number, i: number, self: this) => number, self?: any) {
throw new Error("'map' is an abstract method");
}
public filter(this: number[], cb: (val: number, i: number, self: this) => boolean, self?: any) {
throw new Error("'filter' is an abstract method");
}
public reduce(this: number[], cb: (a: number, b: number, i: number, self: number[]) => number, initial?: number) {
let i = 0;
if (arguments.length <= 1) initial = this[i++];
for (; i < this.length; i++) {
initial = cb(initial!, this[i], i, this);
}
return initial;
}
public some(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (func.invoke(cb, self, [this[i], i, this])) return true;
}
return false;
}
public every(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (!func.invoke(cb, self, [this[i], i, this])) return false;
}
return true;
}
public find(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (func.invoke(cb, self, [this[i], i, this])) return this[i];
}
return undefined;
}
public indexOf(this: number[], val: number, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = start; i < this.length; i++) {
if (this[i] === val) return i;
}
return -1;
}
public lastIndexOf(this: number[], val: number, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = this.length - 1; i >= start; i--) {
if (this[i] === val) return i;
}
return -1;
}
public includes(this: number[], val: number) {
for (let i = 0; i < this.length; i++) {
if (this[i] === val) return i;
}
return false;
}
public sort(this: number[], cb?: (a: number, b: number) => number) {
cb ||= (a, b) => a - b;
return object.sort(this, cb);
}
public reverse(this: number[]) {
const mid = this.length >> 1;
const end = this.length - 1;
for (let i = 0; i < mid; i++) {
const tmp = this[i];
this[i] = this[end - i];
this[end - i] = tmp;
}
return this;
}
public constructor(token?: typeof abstractIgnore) {
if (token !== abstractIgnore) {
throw new TypeError("TypedArray constructor can't be called");
}
}
}

View File

@@ -0,0 +1,32 @@
import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.uint8;
const funcs = typedArrayFuncs(1, factory);
export class Uint8Array extends TypedArray {
public override get byteOffset() {
return funcs.byteOffset(this as any);
}
public override get byteLength() {
return funcs.byteLength(this as any);
}
public override subarray(this: number[], start?: number, end?: number) {
return funcs.subarray(this, start, end);
}
public override slice(this: any[], start?: number, end?: number) {
return funcs.slice(this, start, end);
}
public override map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
return funcs.map(this, cb, self);
}
public override filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
return funcs.filter(this, cb, self);
}
public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore);
return funcs.construct(obj, start, end) as any;
}
}

View File

@@ -0,0 +1,20 @@
import { now, symbol } from "../primordials.ts";
const timeKey: unique symbol = symbol.makeSymbol("") as any;
export const Date = (() => {
class Date {
[timeKey]!: number;
public constructor() {
}
public static now() {
return now();
}
};
return Date as any as typeof Date & ((val?: unknown) => string);
})();
export type Date = InstanceType<typeof Date>;

128
src/lib/libs/classes/map.ts Normal file
View File

@@ -0,0 +1,128 @@
import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts";
import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
export class Map<K, V> {
private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public get(key: K): V {
return this[mapKey].get(key);
}
public has(key: K): boolean {
return this[mapKey].has(key);
}
public set(key: K, val: V) {
this[mapKey].set(key, val);
return this;
}
public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false;
else {
this[mapKey].delete(key);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public keys(): K[] {
return this[mapKey].keys();
}
public values(): V[] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = this[mapKey].get(res[i]);
}
return res;
}
public entries(): [K, V][] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = [res[i], this[mapKey].get(res[i])];
}
return res;
}
public forEach(cb: Function, self?: any) {
const entries = this.entries();
for (let i = 0; i < entries.length; i++) {
func.invoke(cb, self, [entries[i][1], entries[i][0], this]);
}
}
public [symbols.iterator](): Iterator<[K, V]> {
return func.invoke(Array.prototype[symbols.iterator], this.entries(), []) as any;
}
public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map();
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i][0], iterable[i][1]);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value[0], val.value[1]);
}
}
}
}
}
export class WeakMap<K, V> {
private [mapKey]: InstanceType<typeof map>;
public get(key: K): V {
return this[mapKey].get(key);
}
public has(key: K): boolean {
return this[mapKey].has(key);
}
public set(key: K, val: V) {
this[mapKey].set(key, val);
return this;
}
public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false;
else {
this[mapKey].delete(key);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map(true);
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i][0], iterable[i][1]);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value[0], val.value[1]);
}
}
}
}
}
func.setCallable(Map, false);
func.setCallable(WeakMap, false);

View File

@@ -0,0 +1,154 @@
import { func, next, object, symbol } from "../primordials.ts";
enum PromiseState {
Pending = "pend",
Fulfilled = "ful",
Rejected = "rej",
}
const pState: unique symbol = symbol.makeSymbol("Promise.state") as any;
const pValue: unique symbol = symbol.makeSymbol("Promise.value") as any;
const pFulHandles: unique symbol = symbol.makeSymbol("Promise.fulfillHandles") as any;
const pRejHandles: unique symbol = symbol.makeSymbol("Promise.rejectHandles") as any;
function makePromise<T>(): Promise<T> {
return object.setPrototype({
[pState]: PromiseState.Pending,
[pFulHandles]: [],
[pRejHandles]: [],
}, Promise.prototype) as Promise<T>;
}
function fulfill(self: Promise<any>, val: any) {
if (self[pState] !== PromiseState.Pending) return;
if (self === val) throw new Error("A promise may not be fulfilled with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => fulfill(self, val),
(err: any) => reject(self, err),
);
}
else {
self[pValue] = val;
self[pState] = PromiseState.Fulfilled;
const handles = self[pFulHandles]!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
self[pFulHandles] = undefined;
self[pRejHandles] = undefined;
}
}
function reject(self: Promise<any>, val: any) {
if (self[pState] !== PromiseState.Pending) return;
if (self === val) throw new Error("A promise may not be rejected with itself");
if (val != null && typeof val.then === "function") {
val.then(
(val: any) => reject(self, val),
(err: any) => reject(self, err),
);
}
else {
self[pValue] = val;
self[pState] = PromiseState.Rejected;
const handles = self[pRejHandles]!;
for (let i = 0; i < handles.length; i++) {
handles[i](val);
}
self[pFulHandles] = undefined;
self[pRejHandles] = undefined;
}
}
function handle<T>(self: Promise<T>, ful?: (val: T) => void, rej?: (err: any) => void) {
if (self[pState] === PromiseState.Pending) {
if (ful != null) {
self[pFulHandles]![self[pFulHandles]!.length] = ful;
}
if (rej != null) {
self[pRejHandles]![self[pRejHandles]!.length] = rej;
}
}
else if (self[pState] === PromiseState.Fulfilled) {
if (ful != null) ful(self[pValue] as T);
}
else if (self[pState] === PromiseState.Rejected) {
if (rej != null) rej(self[pValue]);
}
}
export class Promise<T> {
public [pState]: PromiseState;
public [pValue]?: T | unknown;
public [pFulHandles]?: ((val: T) => void)[] = [];
public [pRejHandles]?: ((val: T) => void)[] = [];
public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
if (typeof ful !== "function") ful = undefined;
if (typeof rej !== "function") rej = undefined;
const promise = makePromise<Res>();
handle(this,
val => next(() => {
if (ful == null) fulfill(promise, val);
else {
try { fulfill(promise, ful(val)); }
catch (e) { reject(promise, e); }
}
}),
err => next(() => {
if (rej == null) reject(promise, err);
else {
try { fulfill(promise, rej(err)); }
catch (e) { reject(promise, e); }
}
}),
);
return promise;
}
public catch<Res>(rej?: (err: any) => Res) {
return this.then(undefined, rej);
}
public finally(fn?: () => void) {
if (typeof fn !== "function") return this["then"]();
return this.then(
v => {
fn();
return v;
},
v => {
fn();
throw v;
},
)
}
public constructor(fn: (fulfil: (val: T) => void, reject: (err: unknown) => void) => void) {
this[pState] = PromiseState.Pending;
fn(val => fulfill(this, val), err => reject(this, err));
}
public static resolve(val: any) {
const res = makePromise();
fulfill(res, val);
return res;
}
public static reject(val: any) {
const res = makePromise();
reject(res, val);
return res;
}
}
func.setCallable(Promise, false);

121
src/lib/libs/classes/set.ts Normal file
View File

@@ -0,0 +1,121 @@
import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts";
import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
export class Set<T> {
private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public has(key: T): boolean {
return this[mapKey].has(key);
}
public add(val: T) {
this[mapKey].set(val, true);
return this;
}
public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false;
else {
this[mapKey].delete(val);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public keys(): T[] {
return this[mapKey].keys();
}
public values(): T[] {
return this[mapKey].keys();
}
public entries(): [T, T][] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = [res[i], res[i]];
}
return res;
}
public forEach(cb: Function, self?: any) {
const vals = this.values();
for (let i = 0; i < vals.length; i++) {
func.invoke(cb, self, [vals[i], vals[i], this]);
}
}
public [symbols.iterator](): Iterator<T> {
return func.invoke(Array.prototype[symbols.iterator], this.values(), []) as any;
}
public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map();
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i], true);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value, true);
}
}
}
}
}
export class WeakSet<T> {
private [mapKey]: InstanceType<typeof map>;
public has(key: T): boolean {
return this[mapKey].has(key);
}
public add(val: T) {
this[mapKey].set(val, true);
return this;
}
public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false;
else {
this[mapKey].delete(val);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map(true);
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i], true);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value, true);
}
}
}
}
}
func.setCallable(Set, false);
func.setCallable(WeakSet, false);

View File

@@ -0,0 +1,11 @@
import { func, json, object } from "../primordials.ts";
export const console = {};
function method(name: string, func: Function) {
object.defineField(console, name, { c: true, e: false, w: true, v: func });
}
method("log", function log() {
func.invoke(print, null, arguments as any);
});

View File

@@ -0,0 +1,15 @@
import { json, object } from "../primordials.ts";
export const JSON = {};
function method(name: string, func: Function) {
object.defineField(JSON, name, { c: true, e: false, w: true, v: func });
}
method("parse", function parse(val: string) {
return json.parse(val);
});
method("stringify", function stringify(val: string) {
return json.stringify(val);
});

View File

@@ -0,0 +1,60 @@
import { number, object } from "../primordials.ts";
export const Math = {};
function method(name: string, func: Function) {
object.defineField(Math, name, { c: true, e: false, w: true, v: func });
}
method("max", function max() {
let res = -number.Infinity;
for (let i = 0; i < arguments.length; i++) {
if (res < arguments[i]) res = arguments[i];
}
return res;
});
method("min", function min() {
let res = +number.Infinity;
for (let i = 0; i < arguments.length; i++) {
if (res > arguments[i]) res = arguments[i];
}
return res;
});
method("abs", function abs(val: number) {
val = +val;
if (val < 0) return -val;
else return val;
});
method("floor", function floor(val: number) {
val = val - 0;
if (number.isNaN(val)) return number.NaN;
let rem = val % 1;
if (rem < 0) rem += 1;
return val - rem;
});
method("ceil", function floor(val: number) {
val = val - 0;
if (number.isNaN(val)) return number.NaN;
let rem = val % 1;
if (rem === 0) return val;
if (rem < 0) rem += 1;
return val + (1 - rem);
});
method("pow", function pow(a: number, b: number) {
return number.pow(a, b);
});
method("log", function log(val: number) {
return number.log(val);
});

View File

@@ -0,0 +1,5 @@
import { func, object } from "../primordials.ts";
export default function _callSuper(self, constr, args) {
return func.construct(object.getPrototype(constr), func.target(1), args || []);
}

View File

@@ -0,0 +1,5 @@
import { func } from "../primordials.ts";
export default function _classCallCheck() {
if (func.invokeTypeInfer() !== "new") throw new TypeError("Cannot call a class as a function");
}

View File

@@ -0,0 +1,35 @@
import { object } from "../primordials.ts";
function _defineProperties(target, arr) {
if (!arr) return;
for (var i = 0; i < arr.length; i++) {
var desc = arr[i];
var res;
var w, e, c;
c = desc.configurable;
if (c == null) c = true;
e = desc.enumerable;
if (e == null) e = false;
if ("value" in desc) {
w = desc.writable;
if (w == null) w = true;
if (desc.writable == null)
res = object.defineField(target, desc.key, { w: !!w, e: !!e, c: !!c, v: desc.value });
}
else {
res = object.defineProperty(target, desc.key, { e: !!e, c: !!c, g: desc.get, s: desc.set });
}
if (!res) throw "Couldn't set property";
}
}
export default function _createClass(clazz, instance, nonInstance) {
_defineProperties(clazz.prototype, instance);
_defineProperties(clazz, nonInstance);
return clazz;
}

View File

@@ -0,0 +1,7 @@
import { object } from "../primordials.ts";
export default function _defineProperty(obj, key, val) {
if (obj == null) return;
object.defineField(obj, key, { c: true, e: true, w: true, v: val });
return obj;
}

View File

@@ -0,0 +1,5 @@
import { object } from "../primordials.ts";
export default function _getPrototypeOf(obj) {
return object.getPrototype(obj) || null;
}

View File

@@ -0,0 +1,11 @@
import { object } from "../primordials.ts";
export default function _inherits(t, e) {
if (e == null) {
object.setPrototype(t.prototype, undefined);
}
else {
object.setPrototype(t.prototype, e.prototype);
object.setPrototype(t, e);
}
}

View File

@@ -0,0 +1 @@
export default function _possibleConstructorReturn(_, res) { return res; }

View File

@@ -0,0 +1,3 @@
export default function _readOnlyError(name) {
throw name;
}

View File

@@ -0,0 +1,5 @@
import { object } from "../primordials";
export default function _setPrototypeOf(obj, proto) {
object.setPrototype(obj, proto);
}

View File

@@ -0,0 +1,3 @@
export default function _typeof(val) {
return typeof val;
}

136
src/lib/libs/primordials.ts Normal file
View File

@@ -0,0 +1,136 @@
const buffSymbol: unique symbol = undefined as any;
export interface InternalBuffer {
length: number;
[buffSymbol]: "buffer";
}
export interface SymbolPrimordials {
makeSymbol(name: string): symbol;
getSymbol(name: string): symbol;
getSymbolKey(symbol: symbol): string | undefined;
getSymbolDescription(symbol: symbol): string;
}
export interface NumberPrimordials {
NaN: number;
Infinity: number;
PI: number;
E: number;
parseInt(raw: string | number, radix?: number): number;
parseFloat(raw: string | number): number;
isNaN(num: number): boolean;
pow(a: number, b: number): number;
log(val: number): number;
}
export interface StringPrimordials {
stringBuild(parts: string[]): string;
fromCharCode(char: number): string;
fromCodePoint(char: number): string;
toCharCode(char: string): number;
toCodePoint(char: string, i: number): number;
indexOf(str: string, search: string, start: number, reverse?: boolean): number;
substring(str: string, start: number, end: number): string;
lower(str: string): string;
upper(str: string): string;
}
export interface ObjectPrimordials {
defineProperty(obj: object, key: string | number | symbol, conf: { g?: Function, s?: Function, e?: boolean, c?: boolean }): boolean;
defineField(obj: object, key: string | number | symbol, conf: { v?: any, e?: boolean, c?: boolean, w?: boolean }): boolean;
getOwnMember(obj: object, key: any): PropertyDescriptor | undefined;
getOwnMembers(obj: object, onlyEnumerable: boolean): string[];
getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[];
getPrototype(obj: object): object | undefined;
setPrototype(obj: object, proto?: object): object;
preventExt(obj: object): void;
seal(obj: object): void;
freeze(obj: object): void;
isArray(obj: any): obj is any[];
subarray(arr: any[], start: number, end: number): any[];
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
sort(arr: any[], cb: Function): any[];
}
export interface BufferPrimordials {
buff(n: number): InternalBuffer;
backer(arr: number[]): InternalBuffer;
start(arr: number[]): number;
end(arr: number[]): number;
uint8(buff: InternalBuffer, start: number, end: number): number[];
int8(buff: InternalBuffer, start: number, end: number): number[];
int32(buff: InternalBuffer, start: number, end: number): number[];
isUint8(val: any): val is number[];
isInt8(val: any): val is number[];
isInt32(val: any): val is number[];
is(val: any): val is number[];
isBuff(val: any): val is InternalBuffer;
}
export interface FunctionPrimordials {
invokeType(args: IArguments, self: any): "new" | "call";
invokeTypeInfer(): "new" | "call";
target(): Function | null | undefined;
setConstructable(func: Function, flag: boolean): void;
setCallable(func: Function, flag: boolean): void;
invoke(func: Function, self: any, args: any[]): any;
construct(func: Function, self: any, args: any[]): any;
}
export interface JSONPrimordials {
parse(data: string): any;
stringify(data: any): string;
}
export interface Primordials {
symbol: SymbolPrimordials;
number: NumberPrimordials;
string: StringPrimordials;
object: ObjectPrimordials;
function: FunctionPrimordials;
json: JSONPrimordials;
buffer: BufferPrimordials;
map: new (weak?: boolean) => {
get(key: any): any;
has(key: any): boolean;
set(key: any, val: any): void;
delete(key: any): void;
keys(): any[];
clear(): void;
size(): number;
};
regex: new (source: string, multiline?: boolean, noCase?: boolean, dotall?: boolean, unicode?: boolean, unicodeClass?: boolean) => {
exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
groupCount(): number;
};
compile(src: string): Function;
setGlobalPrototypes(prototype: Record<string, any>): void;
now(): number;
next(func: () => void): void;
schedule(func: () => void, delay: number): () => void;
}
globalThis.undefined = void 0;
export const target = (globalThis as any).target;
export const primordials: Primordials = (globalThis as any).primordials;
export const {
symbol,
number,
string,
object,
buffer,
function: func,
json,
map,
regex,
setGlobalPrototypes,
compile,
now,
next,
schedule,
} = primordials;
export type regex = InstanceType<typeof regex>;

26
src/lib/libs/url.ts Normal file
View File

@@ -0,0 +1,26 @@
import { regex, string } from "./primordials";
function escaper(matcher: regex) {
return (text: string) => {
const parts: string[] = [];
let i = 0;
while (true) {
const match = matcher.exec(text, i, false);
if (match == null) break;
const char = match.matches[0];
const code = string.toCharCode(char);
parts[parts.length] = string.substring(text, i, match.matches.index!);
parts[parts.length] = "%" + code;
i = match.end;
}
parts[parts.length] = string.substring(text, i, text.length);
return string.stringBuild(parts);
};
}
export const encodeURI = escaper(new regex("[^A-Za-z0-9\\-+.!~*'()]"));
export const encodeURIComponent = escaper(new regex("[^A-Za-z0-9\\-+.!~*'();/?:@&=+$,#]"));

218
src/lib/libs/utils.ts Normal file
View File

@@ -0,0 +1,218 @@
import { func, string, symbol } from "./primordials.ts";
export const valueKey: unique symbol = symbol.makeSymbol("Primitive.value") as any;
export namespace symbols {
export const asyncIterator: unique symbol = symbol.makeSymbol("Symbol.asyncIterator") as any;
export const iterator: unique symbol = symbol.makeSymbol("Symbol.iterator") as any;
export const match: unique symbol = symbol.makeSymbol("Symbol.match") as any;
export const matchAll: unique symbol = symbol.makeSymbol("Symbol.matchAll") as any;
export const replace: unique symbol = symbol.makeSymbol("Symbol.replace") as any;
export const search: unique symbol = symbol.makeSymbol("Symbol.search") as any;
export const split: unique symbol = symbol.makeSymbol("Symbol.split") as any;
export const toStringTag: unique symbol = symbol.makeSymbol("Symbol.toStringTag") as any;
export const isConcatSpreadable: unique symbol = symbol.makeSymbol("Symbol.isConcatSpreadable") as any;
}
export interface TypeMap {
undefined: undefined;
boolean: boolean;
string: string;
number: number;
symbol: symbol;
object: null | object;
function: Function;
}
export function unwrapThis<T extends keyof TypeMap>(self: any, type: T, constr: Function, name: string, arg = "this", defaultVal?: TypeMap[T]): TypeMap[T] {
if (typeof self === type) return self;
if (self instanceof constr && valueKey in self) self = self[valueKey];
if (typeof self === type) return self;
if (defaultVal !== undefined) return defaultVal;
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
}
export function wrapI(i: number, length: number) {
if (i < 0) return (i + length) | 0;
else return i | 0;
}
export function limitI(i: number, max: number) {
i |= 0;
if (i < 0) return 0;
else if (i > max) return max;
else return i;
}
export type ReplaceRange = { start: number; end: number; matches: string[]; groups?: Record<string, string>; };
type ReplaceLiteral = (string | ((_: { groups: string[]; prev: () => string; next: () => string; }) => string))[];
function parseReplacer(replacer: string, groupN: number) {
const parts: ReplaceLiteral = [];
let lastI = 0;
let lastSlice = 0;
while (true) {
const i = string.indexOf(replacer, "$", lastI);
if (i < 0 || i + 1 >= replacer.length) break;
lastI = i + 1;
switch (replacer[i + 1]) {
case "$":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = "$";
lastSlice = i + 2;
continue;
case "&":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ groups }) => groups[0];
lastSlice = i + 2;
continue;
case "`":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ prev }) => prev();
lastSlice = i + 2;
continue;
case "'":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ next }) => next();
lastSlice = i + 2;
continue;
}
let groupI = 0;
let hasGroup = false;
let consumedN = 1;
while (i + consumedN < replacer.length) {
const code = string.toCharCode(replacer[i + consumedN]);
if (code >= 48 && code <= 57) {
const newGroupI = groupI * 10 + code - 48;
if (newGroupI < 1 || newGroupI >= groupN) break;
groupI = newGroupI;
hasGroup = true;
}
consumedN++;
}
if (hasGroup) {
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ groups }) => groups[groupI];
lastSlice = i + consumedN;
continue;
}
}
if (lastSlice === 0) return [replacer];
else parts[parts.length] = string.substring(replacer, lastSlice, replacer.length);
return parts;
}
function executeReplacer(text: string, match: ReplaceRange, literal: ReplaceLiteral, prevEnd?: number, nextStart?: number) {
const res = [];
for (let i = 0; i < literal.length; i++) {
const curr = literal[i];
if (typeof curr === "function") res[i] = curr({
groups: match.matches,
next: () => string.substring(text, prevEnd ?? 0, match.start),
prev: () => string.substring(text, match.end, nextStart ?? 0),
});
else res[i] = curr;
}
return string.stringBuild(res);
}
export function applyReplaces(text: string, ranges: ReplaceRange[], replace: any, groupN?: number | false) {
if (ranges.length === 0) return text;
const res: string[] = [];
let offset = 0;
if (groupN !== false && typeof replace === "string") {
if (groupN == null) {
for (let i = 0; i < ranges.length; i++) {
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = executeReplacer(text, range, parseReplacer(replace, range.matches.length), prevEnd, nextStart);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
else {
const literal = parseReplacer(replace, groupN);
for (let i = 0; i < ranges.length; i++) {
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = executeReplacer(text, range, literal, prevEnd, nextStart);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
return string.stringBuild(res);
}
if (typeof replace === "string") {
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = replace;
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
else {
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
const args: any[] = range.matches;
args[args.length] = range.start;
args[args.length] = text;
args[args.length] = range.groups;
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = func.invoke(replace, undefined, args);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
return string.stringBuild(res);
}
export function applySplits(text: string, limit: number | undefined, next: (offset: number) => { start: number; end: number; } | undefined) {
let lastEnd = 0;
let lastEmpty = true;
let offset = 0;
const res: string[] = [];
while (true) {
if (limit != null && limit >= 0 && res.length >= limit) break;
const curr = next(offset);
if (curr == null) {
if (!lastEmpty || !res.length) res[res.length] = string.substring(text, lastEnd, text.length);
break;
}
const { start, end } = curr;
const empty = start === end;
if (offset > 0 || !empty) res[res.length] = string.substring(text, lastEnd, start);
lastEnd = end;
offset = empty ? end + 1 : end;
lastEmpty = empty;
}
return res;
}

View File

@@ -0,0 +1,330 @@
import { Error } from "../values/errors.ts";
import { func, object, string } from "../primordials.ts";
import { String } from "../values/string.ts";
import { limitI, symbols, wrapI } from "../utils.ts";
export const Array = (() => {
class Array {
public forEach(this: any[], cb: (val: any, i: number, self: this) => void, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this) func.invoke(cb, self, [this[i], i, this]);
}
}
public join(this: any[], delim = ",") {
delim = String(delim);
const parts = [];
if (delim) {
for (let i = 0; i < this.length; i++) {
if (i) parts[parts.length] = delim;
parts[parts.length] = (i in this) ? String(this[i]) : "";
}
}
else {
for (let i = 0; i < this.length; i++) {
parts[i] = (i in this) ? String(this[i]) : "";
}
}
return string.stringBuild(parts);
}
public push(this: any[]) {
const start = this.length;
for (let i = arguments.length - 1; i >= 0; i--) {
this[start + i] = arguments[i];
}
return arguments.length;
}
public pop(this: any[]) {
if (this.length === 0) return undefined;
else {
const res = this[this.length - 1];
this.length--;
return res;
}
}
public unshift(this: any[]) {
for (let i = this.length + arguments.length - 1; i >= arguments.length; i--) {
this[i] = this[i - arguments.length];
}
for (let i = 0; i < arguments.length; i++) {
this[i] = arguments[i];
}
return arguments.length;
}
public shift(this: any[]) {
if (this.length === 0) return undefined;
const tmp = this[0];
for (let i = 1; i < this.length; i++) {
this[i - 1] = this[i];
}
this.length--;
return tmp;
}
public concat(this: any[]) {
const res: any[] = [];
function add(arr: any) {
if (Array.isArray(arr) || arr != null && typeof arr === "object" && symbols.isConcatSpreadable in arr) {
const start = res.length;
res.length += arr.length;
for (let i = 0; i < res.length; i++) {
if (i in arr) res[start + i] = arr[i];
}
}
else res[res.length] = arr;
}
add(this);
for (let i = 0; i < arguments.length; i++) {
add(arguments[i]);
}
return res;
}
public slice(this: any[], start = 0, end = this.length) {
start = wrapI(start, this.length);
end = wrapI(end, this.length);
if (end <= start) return [];
const res: any[] = [];
res.length = end - start;
for (let i = 0; i < end - start; i++) {
res[i] = this[start + i];
}
return res;
}
public splice(this: any[], start = 0, count = this.length - start) {
const vals: any[] = []
for (let i = 0; i < arguments.length - 2; i++) vals[i] = arguments[i + 2];
start = limitI(wrapI(start, this.length), this.length);
count = limitI(wrapI(count, this.length), this.length - start);
const res: any[] = [];
const change = vals.length - count;
for (let i = start; i < start + count; i++) {
res[i - start] = this[i];
}
if (change < 0) {
for (let i = start - change; i < this.length; i++) {
this[i + change] = this[i];
}
this.length = this.length + change;
}
else {
for (let i = this.length - 1; i >= start - change; i--) {
this[i + change] = this[i];
}
}
for (let i = 0; i < vals.length; i++) {
this[i + start] = vals[i];
}
return res;
}
public map(this: any[], cb: Function, self?: any) {
const res = [];
res.length = this.length;
for (let i = 0; i < this.length; i++) {
if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]);
}
return res;
}
public filter(this: any[], cb: Function, self?: any) {
const res = [];
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i];
}
return res;
}
public reduce(this: any[], cb: Function, initial: any) {
let i = 0;
if (arguments.length <= 1) initial = this[i++];
for (; i < this.length; i++) {
initial = cb(initial, this[i], i, this);
}
return initial;
}
public some(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
}
return false;
}
public every(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && !func.invoke(cb, self, [this[i], i, this])) return false;
}
return true;
}
public find(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) return this[i];
}
return undefined;
}
public indexOf(this: any[], val: any, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = start; i < this.length; i++) {
if (i in this && this[i] === val) return i;
}
return -1;
}
public lastIndexOf(this: any[], val: any, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = this.length - 1; i >= start; i--) {
if (i in this && this[i] === val) return i;
}
return -1;
}
public includes(this: any[], val: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && this[i] === val) return true;
}
return false;
}
public sort(this: any[], cb?: Function) {
cb ||= (a: any, b: any) => {
if (String(a) < String(b)) return -1;
if (String(a) === String(b)) return 0;
return 1;
};
return object.sort(this, cb);
}
public reverse(this: any[]) {
const mid = this.length >> 1;
const end = this.length - 1;
for (let i = 0; i < mid; i++) {
const tmp = this[i];
this[i] = this[end - i];
this[end - i] = tmp;
}
return this;
}
public [symbols.iterator](this: any[]) {
let i = 0;
let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []);
return {
next() {
if (arr == null) return { done: true, value: undefined };
if (i >= arr.length) {
arr = undefined;
return { done: true, value: undefined };
}
while (true) {
const res = arr![i];
if (i in arr!) {
i++;
return { done: false, value: res };
}
else i++;
}
},
[symbols.iterator]() { return this; }
};
}
public constructor (len: unknown) {
if (arguments.length === 1 && typeof len === "number") {
const res: any[] = [];
res.length = len;
return res as any;
}
else {
const res: any[] = [];
res.length = arguments.length;
for (let i = 0; i < arguments.length; i++) {
res[i] = arguments[i];
}
return res as any;
}
}
public static isArray(val: any): val is any[] {
return object.isArray(val);
}
public static from(val: any, cb?: Function, self?: any): any[] {
if (symbols.iterator in val) {
const res = [];
const it = val[symbols.iterator]();
if (cb) {
for (let val = it.next(); !val.done; val = it.next()) {
res[res.length] = func.invoke(cb, self, [val.value]);
}
}
else {
for (let val = it.next(); !val.done; val = it.next()) {
res[res.length] = val.value;
}
}
return res;
}
else if ("length" in val) {
const res = [];
if (cb) {
for (let i = 0; i < val.length; i++) {
if (i in val) res[i] = func.invoke(cb, self, [val[i]]);
}
}
else {
for (let i = 0; i < val.length; i++) {
if (i in val) res[i] = val[i];
}
}
return res;
}
else if (val == null) throw new Error("Illegal argument");
else return [];
}
}
func.setCallable(Array, true);
func.setConstructable(Array, true);
return Array as any as typeof Array & ((value?: unknown) => object);
})();
export type Array = InstanceType<typeof Array>;

View File

@@ -0,0 +1,29 @@
import { func } from "../primordials.ts";
import { unwrapThis, valueKey } from "../utils.ts";
export const Boolean = (() => {
class Boolean {
[valueKey]!: boolean;
public toString() {
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
}
public valueOf() {
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
}
public constructor(value?: unknown) {
if (func.invokeType(arguments, this) === "call") {
if (arguments.length === 0) return false as any;
else return !!value as any;
}
this[valueKey] = (Boolean as any)(value);
}
};
func.setCallable(Boolean, true);
func.setConstructable(Boolean, true);
return Boolean as any as typeof Boolean & ((value?: unknown) => symbol);
})();
export type Boolean = InstanceType<typeof Boolean>;

View File

@@ -0,0 +1,39 @@
import { func, object } from "../primordials.ts";
import { String } from "./string.ts";
export class Error {
public declare name: string;
public declare message: string;
public toString() {
let res = this.name || "Error";
const msg = this.message;
if (msg) res += ": " + msg;
return res;
}
public constructor (msg = "") {
if (func.invokeType(arguments, this) === "call") return new Error(msg);
this.message = String(msg);
}
}
object.defineField(Error.prototype, "name", { c: true, e: false, w: true, v: "Error" });
object.defineField(Error.prototype, "message", { c: true, e: false, w: true, v: "" });
func.setCallable(Error, true);
func.setConstructable(Error, true);
export class SyntaxError extends Error { }
object.defineField(SyntaxError.prototype, "name", { c: true, e: false, w: true, v: "SyntaxError" });
func.setCallable(SyntaxError, true);
func.setConstructable(SyntaxError, true);
export class TypeError extends Error { }
object.defineField(TypeError.prototype, "name", { c: true, e: false, w: true, v: "TypeError" });
func.setCallable(TypeError, true);
func.setConstructable(TypeError, true);
export class RangeError extends Error { }
object.defineField(RangeError.prototype, "name", { c: true, e: false, w: true, v: "RangeError" });
func.setCallable(RangeError, true);
func.setConstructable(RangeError, true);

View File

@@ -0,0 +1,82 @@
import { compile, func, string } from "../primordials.ts";
import { String } from "./string.ts";
export const Function = (() => {
class Function {
declare public readonly name: string;
declare public readonly length: number;
public toString(this: Function) {
if (this.name !== "") return "function " + this.name + "(...) { ... }";
else return "function (...) { ... }";
}
public valueOf() {
return this;
}
public apply(this: (...args: any) => any, self: any, args: any[]) {
return func.invoke(this, self, args);
}
public call(this: (...args: any) => any, self: any) {
const args: any[] = [];
for (let i = arguments.length - 1; i >= 1; i--) args[i - 1] = arguments[i];
return func.invoke(this, self, args);
}
public bind(this: (...args: any) => any, self: any) {
const cb = this;
if (arguments.length === 0) return function (this: any) { return func.invoke(cb, this, arguments as any) };
if (arguments.length <= 1) return function () { return func.invoke(cb, self, arguments as any); }
const base: any[] = [];
const offset = arguments.length - 1;
base.length = offset;
for (let i = 0; i < offset; i++) base[i] = arguments[i + 1];
return function () {
for (let i = 0; i < arguments.length; i++) {
base[offset + i] = arguments[i];
}
return func.invoke(cb, self, base);
};
}
public constructor () {
const parts = ["(function anonymous("];
for (let i = 0; i < arguments.length - 1; i++) {
if (i > 0) parts[parts.length] = ",";
parts[parts.length] = arguments[i];
}
parts[parts.length] = "){\n";
parts[parts.length] = String(arguments[arguments.length - 1]);
parts[parts.length] = "\n})";
var res = compile(string.stringBuild(parts))();
return res;
}
public static compile(src = "", { globals = [], wrap = false }: { globals?: string[], wrap?: boolean } = {}) {
const parts = [];
if (wrap) parts[parts.length] = "return (function() {\n";
if (globals.length > 0) {
parts[parts.length] = "let {";
for (let i = 0; i < globals.length; i++) {
if (i > 0) parts[parts.length] = ",";
parts[parts.length] = globals[i];
}
parts[parts.length] = "} = arguments[0];";
}
parts[parts.length] = src;
if (wrap) parts[parts.length] = "\n})(arguments[0])";
const res = compile(string.stringBuild(parts));
return res;
}
}
func.setCallable(Function, true);
func.setConstructable(Function, true);
return Function as any as typeof Function & ((value?: unknown) => (...args: any[]) => any);
})();

View File

@@ -0,0 +1,77 @@
import { func, number, object } from "../primordials.ts";
import { unwrapThis, valueKey } from "../utils.ts";
export const Number = (() => {
class Number {
[valueKey]!: number;
public toString() {
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
}
public valueOf() {
return unwrapThis(this, "number", Number, "Number.prototype.toString");
}
public constructor (value?: unknown) {
if (func.invokeType(arguments, this) === "call") {
if (arguments.length === 0) return 0 as any;
else return +(value as any) as any;
}
this[valueKey] = (Number as any)(value);
}
public static isFinite(value: number) {
value = unwrapThis(value, "number", Number, "Number.isFinite", "value");
if (value === undefined || value !== value) return false;
if (value === number.Infinity || value === -number.Infinity) return false;
return true;
}
public static isInteger(value: number) {
value = unwrapThis(value, "number", Number, "Number.isInteger", "value");
if (value === undefined) return false;
return number.parseInt(value) === value;
}
public static isNaN(value: number) {
return number.isNaN(value);
}
public static isSafeInteger(value: number) {
value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value");
if (value === undefined || number.parseInt(value) !== value) return false;
return value >= -9007199254740991 && value <= 9007199254740991;
}
public static parseFloat(value: unknown) {
if (typeof value === "number") return value;
else return number.parseFloat(value + "");
}
public static parseInt(value: unknown, radix = 10) {
radix = +radix;
if (number.isNaN(radix)) radix = 10;
if (typeof value === "number") return number.parseInt(value, radix);
else return number.parseInt(value + "", radix);
}
declare public static readonly EPSILON: number;
declare public static readonly MIN_SAFE_INTEGER: number;
declare public static readonly MAX_SAFE_INTEGER: number;
declare public static readonly POSITIVE_INFINITY: number;
declare public static readonly NEGATIVE_INFINITY: number;
declare public static readonly NaN: number;
declare public static readonly MAX_VALUE: number;
declare public static readonly MIN_VALUE: number;
}
object.defineField(Number, "EPSILON", { c: false, e: false, w: false, v: 2.220446049250313e-16 });
object.defineField(Number, "MIN_SAFE_INTEGER", { c: false, e: false, w: false, v: -9007199254740991 });
object.defineField(Number, "MAX_SAFE_INTEGER", { c: false, e: false, w: false, v: 9007199254740991 });
object.defineField(Number, "POSITIVE_INFINITY", { c: false, e: false, w: false, v: +number.Infinity });
object.defineField(Number, "NEGATIVE_INFINITY", { c: false, e: false, w: false, v: -number.Infinity });
object.defineField(Number, "NaN", { c: false, e: false, w: false, v: number.NaN });
object.defineField(Number, "MAX_VALUE", { c: false, e: false, w: false, v: 1.7976931348623157e+308 });
object.defineField(Number, "MIN_VALUE", { c: false, e: false, w: false, v: 5e-324 });
func.setCallable(Number, true);
func.setConstructable(Number, true);
return Number as any as typeof Number & ((value?: unknown) => number);
})();
export type Number = InstanceType<typeof Number>;

View File

@@ -0,0 +1,208 @@
import { Boolean } from "./boolean.ts";
import { TypeError } from "./errors.ts";
import { Number } from "./number.ts";
import { func, object } from "../primordials.ts";
import { String } from "./string.ts";
import { symbols, valueKey } from "../utils.ts";
import { Symbol } from "../values/symbol.ts";
export const Object = (() => {
class Object {
public toString(this: unknown) {
if (this === undefined) return "[object Undefined]";
else if (this === null) return "[object Null]";
else if (typeof this === "object") {
if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]";
else if (object.isArray(this)) return "[object Array]";
else return "[object Object]";
}
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
else if (typeof this === "string" || this instanceof String) return "[object String]";
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
else if (typeof this === "function") return "[object Function]";
}
public valueOf() {
return this;
}
public hasOwnProperty(key: string) {
return object.getOwnMember(this, key) != null;
}
public get __proto__() {
return object.getPrototype(this);
}
public set __proto__(val) {
object.setPrototype(this, val);
}
public constructor (value?: unknown) {
if (typeof value === 'object' && value !== null) return value as any;
if (typeof value === 'string') return new String(value) as any;
if (typeof value === 'number') return new Number(value) as any;
if (typeof value === 'boolean') return new Boolean(value) as any;
if (typeof value === 'symbol') {
const res: Symbol = {} as any;
object.setPrototype(res, Symbol.prototype);
res[valueKey] = value;
return res as any;
}
return {} as any;
}
public static getOwnPropertyDescriptor(obj: object, key: any) {
return object.getOwnMember(obj, key);
}
public static getOwnPropertyNames(obj: object): string[] {
return object.getOwnMembers(obj, false);
}
public static getOwnPropertySymbols(obj: object): symbol[] {
return object.getOwnSymbolMembers(obj, false);
}
public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) {
if (obj === null || typeof obj !== "function" && typeof obj !== "object") {
throw new TypeError("Object.defineProperty called on non-object");
}
if (desc === null || typeof desc !== "function" && typeof desc !== "object") {
throw new TypeError("Property description must be an object: " + desc);
}
const res: any = {};
if ("get" in desc || "set" in desc) {
if ("get" in desc) {
const get = desc.get;
if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get);
res.g = get;
}
if ("set" in desc) {
const set = desc.set;
if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set);
res.s = set;
}
if ("enumerable" in desc) res.e = !!desc.enumerable;
if ("configurable" in desc) res.e = !!desc.configurable;
if (!object.defineProperty(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
}
else {
if ("enumerable" in desc) res.e = !!desc.enumerable;
if ("configurable" in desc) res.e = !!desc.configurable;
if ("writable" in desc) res.w = !!desc.writable;
if ("value" in desc) res.v = desc.value;
if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
}
return obj;
}
public static defineProperties(obj: object, desc: PropertyDescriptorMap) {
const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[];
const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[];
for (let i = 0; i < keys.length; i++) {
Object.defineProperty(obj, keys[i], desc[keys[i]]);
}
for (let i = 0; i < symbols.length; i++) {
Object.defineProperty(obj, symbols[i], desc[symbols[i]]);
}
return obj;
}
public static create(proto: object, desc?: PropertyDescriptorMap) {
let res = object.setPrototype({}, proto);
if (desc != null) this.defineProperties(res, desc);
return res;
}
public static assign(target: any) {
for (let i = 1; i < arguments.length; i++) {
const obj = arguments[i];
const keys = object.getOwnMembers(obj, false);
const symbols = object.getOwnSymbolMembers(obj, false);
for (let j = 0; j < keys.length; j++) {
target[keys[j]] = obj[keys[j]];
}
for (let j = 0; j < symbols.length; j++) {
target[symbols[j]] = obj[symbols[j]];
}
}
return target;
}
public static setPrototypeOf(obj: object, proto: object | null) {
object.setPrototype(obj, proto!);
}
public static getPrototypeOf(obj: object) {
return object.getPrototype(obj) || null;
}
public static keys(obj: any) {
const res: any[] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = keys[i];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = symbols[i];
}
return res;
}
public static values(obj: any) {
const res: any[] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = obj[keys[i]];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = obj[symbols[i]];
}
return res;
}
public static entries(obj: any) {
const res: [any, any][] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = [keys[i], obj[keys[i]]];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = [symbols[i], obj[symbols[i]]];
}
return res;
}
public static preventExtensions(obj: object) {
object.preventExt(obj);
return obj;
}
public static seal(obj: object) {
object.seal(obj);
return obj;
}
public static freeze(obj: object) {
object.freeze(obj);
return obj;
}
}
object.defineProperty(Object.prototype, "__proto__", { e: false });
object.setPrototype(Object.prototype, undefined);
func.setCallable(Object, true);
func.setConstructable(Object, true);
return Object as any as typeof Object & ((value?: unknown) => object);
})();
export type Object = InstanceType<typeof Object>;

View File

@@ -0,0 +1,138 @@
import { func, regex, symbol } from "../primordials.ts";
import { String } from "./string.ts";
import { type ReplaceRange } from "../utils.ts";
import { applyReplaces } from "../utils.ts";
import { applySplits } from "../utils.ts";
import { symbols } from "../utils.ts";
const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any;
export class RegExp {
private [regexKey]!: InstanceType<typeof regex>;
public readonly source!: string;
public readonly flags!: string;
public lastIndex = 0;
public readonly indices!: boolean;
public readonly global!: boolean;
public readonly ignoreCase!: boolean;
public readonly multiline!: boolean;
public readonly dotall!: boolean;
public readonly unicode!: boolean;
public readonly unicodeSets!: boolean;
public readonly sticky!: boolean;
public constructor(source: any, flags = "") {
if (func.invokeType(arguments, this) === "call") return new RegExp(source, flags);
source = this.source = String(typeof source === "object" && "source" in source ? source.source : source);
flags = String(flags);
let indices = false;
let global = false;
let ignoreCase = false;
let multiline = false;
let dotall = false;
let unicode = false;
let unicodeSets = false;
let sticky = false;
for (let i = 0; i < flags.length; i++) {
switch (flags[i]) {
case "d": indices = true; break;
case "g": global = true; break;
case "i": ignoreCase = true; break;
case "m": multiline = true; break;
case "s": dotall = true; break;
case "u": unicode = true; break;
case "v": unicodeSets = true; break;
case "y": sticky = true; break;
}
}
flags = "";
if (indices) flags += "d";
if (global) flags += "g";
if (ignoreCase) flags += "i";
if (multiline) flags += "m";
if (dotall) flags += "s";
if (unicode) flags += "u";
if (unicodeSets) flags += "v";
if (sticky) flags += "y";
this.flags = flags;
this.indices = indices;
this.global = global;
this.ignoreCase = ignoreCase;
this.multiline = multiline;
this.dotall = dotall;
this.unicode = unicode;
this.unicodeSets = unicodeSets;
this.sticky = sticky;
this[regexKey] = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets);
}
public exec(target: string) {
const useLast = this.global || this.sticky;
const start = useLast ? this.lastIndex : 0;
const match = this[regexKey].exec(target, start, this.indices);
if (match != null && !(this.sticky && match.matches.index !== start)) {
if (useLast) this.lastIndex = match.end;
return match.matches;
}
if (useLast) this.lastIndex = 0;
return null;
}
public test(target: string) {
return this.exec(target) != null;
}
public [symbols.split](target: string, limit?: number) {
return applySplits(target, limit, offset => {
const val = this[regexKey].exec(target, offset, false);
if (val == null) return undefined;
return { start: val.matches.index!, end: val.end };
});
}
public [symbols.replace](target: string, replacer: any) {
const matches: ReplaceRange[] = [];
const regex = this[regexKey];
if (this.global) {
let offset = 0;
while (true) {
const match = regex.exec(target, offset, false);
if (match == null) break;
const start = match.matches.index;
const end = match.end;
const arr: string[] = [];
for (let i = 0; i < match.matches.length; i++) {
arr[i] = match.matches[i];
}
matches[matches.length] = { start: match.matches.index!, end: match.end, matches: arr };
if (start === end) offset = start + 1;
else offset = end;
}
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
}
else {
const match = this.exec(target);
if (match != null) matches[0] = {
start: match.index!,
end: match.index! + match[0].length,
matches: match,
}
}
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
}
}

View File

@@ -0,0 +1,276 @@
import { TypeError } from "./errors.ts";
import { func, number, regex, string } from "../primordials.ts";
import { RegExp } from "./regex.ts";
import { applyReplaces, applySplits, limitI, type ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "../utils.ts";
const trimStartRegex = new regex("^\\s+", false, false, false, false, false);
const trimEndRegex = new regex("\\s+$", false, false, false, false, false);
export const String = (() => {
class String {
[valueKey]!: string;
public at(index: number) {
throw "Not implemented :/";
return unwrapThis(this, "string", String, "String.prototype.at")[index];
}
public toString() {
return unwrapThis(this, "string", String, "String.prototype.toString");
}
public valueOf() {
return unwrapThis(this, "string", String, "String.prototype.valueOf");
}
public includes(search: string, offset = 0) {
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
return string.indexOf(self, (String as any)(search), +offset, false) >= 0;
}
public startsWith(search: string) {
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
if (self.length < search.length) return false;
return string.substring(self, 0, search.length) === search;
}
public endsWith(search: string) {
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
if (self.length < search.length) return false;
return string.substring(self, self.length - search.length, self.length) === search;
}
public indexOf(search: string, offset = 0) {
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
offset = +offset;
return string.indexOf(self, search, offset, false);
}
public lastIndexOf(search: string, offset = 0) {
const self = unwrapThis(this, "string", String, "String.prototype.lastIndexOf");
offset = +offset;
return string.indexOf(self, search, offset, true);
}
public trim() {
const self = unwrapThis(this, "string", String, "String.prototype.trim");
const start = trimStartRegex.exec(self, 0, false);
const end = trimEndRegex.exec(self, 0, false);
const startI = start == null ? 0 : start.end;
const endI = end == null ? self.length : end.matches.index!;
return string.substring(self, startI, endI);
}
public trimStart() {
const self = unwrapThis(this, "string", String, "String.prototype.trim");
const start = trimStartRegex.exec(self, 0, false);
const startI = start == null ? 0 : start.end;
return string.substring(self, startI, self.length);
}
public trimEnd() {
const self = unwrapThis(this, "string", String, "String.prototype.trim");
const end = trimEndRegex.exec(self, 0, false);
const endI = end == null ? self.length : end.matches.index!;
return string.substring(self, 0, endI);
}
public trimLeft() {
return func.invoke(String.prototype.trimStart, this, []);
}
public trimRight() {
return func.invoke(String.prototype.trimEnd, this, []);
}
public charAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charAt");
return self[i];
}
public charCodeAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
return self[i] ? string.toCharCode(self[i]) : number.NaN;
}
public codePointAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
return i >= 0 && i < self.length ? string.toCodePoint(self, i) : number.NaN;
}
public split(val?: any, limit?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.split");
if (val === undefined) return [self];
if (val !== null && typeof val === "object" && symbols.split in val) {
return val[symbols.split](self, limit);
}
val = (String as any)(val);
return applySplits(self, limit, offset => {
const start = string.indexOf(self, val, offset, false);
if (start < 0) return undefined;
else return { start, end: start + val.length };
});
}
public replace(val: any, replacer: any) {
const self = unwrapThis(this, "string", String, "String.prototype.replace");
if (val !== null && typeof val === "object" && symbols.replace in val) {
return val[symbols.replace](self, replacer);
}
else val = (String as any)(val);
const i = string.indexOf(self, val, 0);
return applyReplaces(self, [{ start: i, end: i + val.length, matches: [val] }], replacer, false);
}
public replaceAll(val: any, replacer: any) {
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
if (val !== null && typeof val === "object" && symbols.replace in val) {
if (val instanceof RegExp && !val.global) throw new TypeError("replaceAll must be called with a global RegExp");
return val[symbols.replace](self, replacer);
}
else val = (String as any)(val);
let offset = 0;
const matches: ReplaceRange[] = [];
const add = val.length === 0 ? 1 : val.length;
while (true) {
const i = string.indexOf(self, val, offset);
if (i < 0) break;
matches[matches.length] = { start: i, end: i + val.length, matches: [val] };
if (val.length === 0)
offset = i + add;
}
return applyReplaces(self, matches, replacer, false);
}
public repeat(n: number) {
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
const res: string[] = [];
for (let i = 0; i < n; i++) {
res[i] = self;
}
return string.stringBuild(res);
}
public slice(start = 0, end?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.slice");
if (end === undefined) end = self.length;
start = limitI(wrapI(start, self.length), self.length);
end = limitI(wrapI(end, self.length), self.length);
if (end <= start) return "";
return string.substring(self, start, end);
}
public substring(start = 0, end?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.substring");
if (end === undefined) end = self.length;
start = limitI(start, self.length);
end = limitI(end, self.length);
if (end <= start) return "";
return string.substring(self, start, end);
}
public substr(start = 0, count?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.substr");
count = self.length - start;
start = limitI(start, self.length);
count = limitI(count, self.length - start);
if (count <= 0) return "";
return string.substring(self, start, count + start);
}
public concat() {
const self = unwrapThis(this, "string", String, "String.prototype.concat");
const parts = [self];
for (let i = 0; i < arguments.length; i++) {
parts[i + 1] = (String as any)(arguments[i]);
}
return string.stringBuild(parts);
}
public toLowerCase() {
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
return string.lower(self);
}
public toUpperCase() {
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
return string.upper(self);
}
public match(regex: RegExp) {
const self = unwrapThis(this, "string", String, "String.prototype.match");
if (!(regex instanceof RegExp)) throw new TypeError("Regexp expected for String.prototype.match");
if (regex.global) {
let matches: string[] | null = null;
while (true) {
const match = regex.exec(self);
if (match == null) break;
matches ||= [];
matches[matches.length] = match[0];
}
return matches;
}
else return regex.exec(self);
}
public [symbols.iterator]() {
var i = 0;
var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]");
return {
next () {
if (arr == null) return { done: true, value: undefined };
if (i > arr.length) {
arr = undefined;
return { done: true, value: undefined };
}
else {
var val = arr[i++];
if (i >= arr.length) arr = undefined;
return { done: false, value: val };
}
},
[symbols.iterator]() { return this; }
};
}
public constructor (value?: unknown) {
if (func.invokeType(arguments, this) === "call") {
if (arguments.length === 0) return "" as any;
else if (typeof value === "symbol") return value.toString() as any;
else return (value as any) + "" as any;
}
this[valueKey] = (String as any)(value);
}
public static fromCharCode() {
const res: string[] = [];
res[arguments.length] = "";
for (let i = 0; i < arguments.length; i++) {
res[i] = string.fromCharCode(+arguments[i]);
}
return string.stringBuild(res);
}
public static fromCodePoint() {
const res: string[] = [];
res[arguments.length] = "";
for (var i = 0; i < arguments.length; i++) {
res[i] = string.fromCodePoint(+arguments[i]);
}
return string.stringBuild(res);
}
}
func.setCallable(String, true);
func.setConstructable(String, true);
return String as any as typeof String & ((value?: unknown) => string);
})();
export type String = InstanceType<typeof String>;

View File

@@ -0,0 +1,53 @@
import { func, object, symbol } from "../primordials.ts";
import { symbols, unwrapThis, valueKey } from "../utils.ts";
export const Symbol = (() => {
class Symbol {
[valueKey]!: symbol;
get description() {
return symbol.getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
}
public toString() {
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
}
public valueOf() {
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
}
public constructor(name = "") {
return symbol.makeSymbol(name + "") as any;
}
public static for(name: string) {
return symbol.getSymbol(name + "");
}
declare public static readonly asyncIterator: unique symbol;
declare public static readonly iterator: unique symbol;
declare public static readonly match: unique symbol;
declare public static readonly matchAll: unique symbol;
declare public static readonly replace: unique symbol;
declare public static readonly search: unique symbol;
declare public static readonly split: unique symbol;
declare public static readonly toStringTag: unique symbol;
};
func.setCallable(Symbol, true);
func.setConstructable(Symbol, false);
object.defineField(Symbol, "asyncIterator", { c: false, e: false, w: false, v: symbols.asyncIterator });
object.defineField(Symbol, "iterator", { c: false, e: false, w: false, v: symbols.iterator });
object.defineField(Symbol, "match", { c: false, e: false, w: false, v: symbols.match });
object.defineField(Symbol, "matchAll", { c: false, e: false, w: false, v: symbols.matchAll });
object.defineField(Symbol, "replace", { c: false, e: false, w: false, v: symbols.replace });
object.defineField(Symbol, "search", { c: false, e: false, w: false, v: symbols.search });
object.defineField(Symbol, "split", { c: false, e: false, w: false, v: symbols.split });
object.defineField(Symbol, "toStringTag", { c: false, e: false, w: false, v: symbols.toStringTag });
object.defineField(Symbol, "isConcatSpreadable", { c: false, e: false, w: false, v: symbols.isConcatSpreadable });
return Symbol as any as typeof Symbol & ((name?: string) => ReturnType<SymbolConstructor>);
})();
export type Symbol = InstanceType<typeof Symbol>;

View File

@@ -0,0 +1,7 @@
// import coffeescript from "./coffeescript.ts";
// import babel from "./babel.ts";
// register(v => coffeescript(babel(v)));
import typescript from "./typescript.ts";
register(typescript);

View File

@@ -0,0 +1,27 @@
import { SourceMap } from "./map.ts";
import { transform, availablePresets } from "@babel/standalone";
export default function babel(next: Compiler): Compiler {
print("Loaded babel!");
return (filename, code, prevMap) => {
const res = transform(code, {
filename,
sourceMaps: true,
presets: [availablePresets.env],
});
print(res.map!.mappings);
const map = SourceMap.parse({
file: "babel-internal://" + filename,
mappings: res.map!.mappings,
sources: [filename],
});
const compiled = next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
registerSource(filename, code);
return compiled;
};
}

View File

@@ -0,0 +1,28 @@
import { compile } from "coffeescript";
import { SourceMap } from "./map.ts";
export default function coffee(next: Compiler): Compiler {
print("Loaded coffeescript!");
return (filename, code, prevMap) => {
const {
js: result,
v3SourceMap: rawMap,
} = compile(code, {
filename,
sourceMap: true,
bare: true,
});
const map = SourceMap.parse({
file: "coffee-internal://" + filename,
mappings: JSON.parse(rawMap).mappings,
sources: [filename],
});
const compiled = next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
registerSource(filename, code);
return compiled;
};
}

190
src/lib/transpiler/map.ts Normal file
View File

@@ -0,0 +1,190 @@
const map: number[] = [];
let j = 0;
for (let i = 65; i <= 90; i++) map[i] = j++;
for (let i = 97; i <= 122; i++) map[i] = j++;
map[43] = j++;
map[47] = j++;
export function decodeVLQ(val: string): number[][][] {
const lines: number[][][] = [];
const fileParts = val.split(";", -1);
for (let i = 0; i < fileParts.length; i++) {
const line = fileParts[i];
if (line.length === 0) {
lines.push([]);
continue;
}
const elements: number[][] = [];
const lineParts = line.split(",", -1);
for (let i = 0; i < lineParts.length; i++) {
const el = lineParts[i];
if (el.length === 0) elements.push([]);
else {
const list: number[] = [];
for (let i = 0; i < el.length;) {
let sign = 1;
let curr = map[el.charCodeAt(i++)];
let cont = (curr & 0x20) === 0x20;
if ((curr & 1) === 1) sign = -1;
let res = (curr & 0b11110) >> 1;
let n = 4;
for (; i < el.length && cont;) {
curr = map[el.charCodeAt(i++)];
cont = (curr & 0x20) == 0x20;
res |= (curr & 0b11111) << n;
n += 5;
if (!cont) break;
}
list.push(res * sign);
}
elements.push(list);
}
}
lines.push(elements);
}
return lines;
}
export namespace Location {
export function compare(a: Location, b: Location) {
const { 0: filenameA, 1: lineA, 2: startA } = a;
const { 0: filenameB, 1: lineB, 2: startB } = b;
if (filenameA < filenameB) return -1;
if (filenameA > filenameB) return 1;
const lineI = lineA - lineB;
if (lineI !== 0) return lineI;
return startA - startB;
}
export function comparePoints(a: Location, b: Location) {
const { 1: lineA, 2: startA } = a;
const { 1: lineB, 2: startB } = b;
const lineI = lineA - lineB;
if (lineI !== 0) return lineI;
return startA - startB;
}
}
export class VLQSourceMap {
public constructor(
public readonly array: Map<string, [start: number, dst: Location][][]>,
) { }
public converter() {
return (src: Location) => {
const file = this.array.get(src[0]);
if (file == null) return src;
const line = file[src[1]];
if (line == null || line.length === 0) return undefined;
let a = 0;
let b = line.length;
while (true) {
const done = b - a <= 1;
const mid = (a + b) >> 1;
const el = line[mid];
const cmp = el[0] - src[2];
if (cmp < 0) {
if (done) {
if (b >= line.length) return undefined;
break;
}
a = mid;
}
else if (cmp > 0) {
if (done) {
if (a <= 0) return undefined;
break;
}
b = mid;
}
else return el[1];
}
return line[b][1];
};
}
public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap {
const mapping = decodeVLQ(raw);
const file: [start: number, dst: Location][][] = [];
const res = new Map<string, [start: number, dst: Location][][]>([[compiled, file]]);
let originalRow = 0;
let originalCol = 0;
let originalFile = 0;
const lastCols = new Set<number>();
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
const line: [start: number, dst: Location][] = file[compiledRow] = [];
let compiledCol = 0;
const rowData = mapping[compiledRow];
for (let i = 0; i < rowData.length; i++) {
const rawSeg = rowData[i];
compiledCol += rawSeg.length > 0 ? rawSeg[0] : 0;
originalFile += rawSeg.length > 1 ? rawSeg[1] : 0;
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
if (!lastCols.has(compiledCol)) {
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
}
lastCols.add(compiledCol);
}
line.sort((a, b) => a[0] - b[0]);
}
return new VLQSourceMap(res);
}
public static parse(raw: string | { file: string, mappings: string, sources: string[] }) {
if (typeof raw === "string") raw = JSON.parse(raw) as { file: string, mappings: string, sources: string[] };
const compiled = raw.file;
const mapping = raw.mappings;
let filenames = raw.sources;
if (filenames.length === 0 || filenames.length === 1 && filenames[0] === "") filenames = [compiled];
return this.parseVLQ(compiled, filenames, mapping);
}
}
export namespace SourceMap {
export function parse(raw: string | { file: string, mappings: string, sources: string[] }) {
return VLQSourceMap.parse(raw).converter();
}
export function chain(...maps: SourceMap[]): SourceMap {
return loc => {
for (const el of maps) {
const tmp = el(loc);
if (tmp == null) return undefined;
else loc = tmp;
}
return loc;
};
}
}

10
src/lib/transpiler/transpiler.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
type Location = readonly [file: string, line: number, start: number];
type SourceMap = (loc: Location) => Location | undefined;
type CompilerFactory = (next: Compiler) => Compiler;
type Compiler = (filename: string, src: string, mapper: SourceMap) => (this: any, ...args: any[]) => any;
declare function getResource(name: string): string | undefined;
declare function print(...args: any[]): void;
declare function register(factory: CompilerFactory): void;
declare function registerSource(filename: string, src: string): void;

View File

@@ -0,0 +1,118 @@
import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost, LanguageService } from "typescript";
import { SourceMap } from "./map.ts";
const resources: Record<string, string | undefined> = {};
function resource(name: string) {
if (name in resources) return resources[name];
else return resources[name] = getResource(name);
}
export default function typescript(next: Compiler): Compiler {
const files: Record<string, IScriptSnapshot> = {};
const versions: Record<string, number> = {};
let declI = 0;
const settings: CompilerOptions = {
target: ScriptTarget.ES5,
module: ModuleKind.Preserve,
allowImportingTsExtensions: true,
verbatimModuleSyntax: true,
strict: false,
skipLibCheck: true,
forceConsistentCasingInFileNames: true,
declaration: true,
sourceMap: true,
downlevelIteration: true,
};
let service: LanguageService;
measure(() => {
service = createLanguageService({
getCurrentDirectory: () => "/",
getDefaultLibFileName: () => "/lib.d.ts",
getScriptFileNames: () => {
const res = ["/src.ts", "/lib.d.ts"];
for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts");
return res;
},
getCompilationSettings: () => settings,
log: print,
fileExists: filename => filename in files || resource(filename) != null,
getScriptSnapshot: (filename) => {
if (filename in files) return files[filename];
else {
const src = resource(filename);
if (src == null) return undefined;
return files[filename] = ScriptSnapshot.fromString(src);
}
},
getScriptVersion: (filename) => String(versions[filename] || 0),
readFile: () => { throw "no"; },
writeFile: () => { throw "no"; },
}, createDocumentRegistry());
});
measure(() => {
service.getEmitOutput("/lib.d.ts");
});
print("Loaded typescript!");
return (filename, code, prevMap) => {
files["/src.ts"] = ScriptSnapshot.fromString(code);
versions["/src.ts"] ??= 0;
versions["/src.ts"]++;
const emit = service.getEmitOutput("/src.ts");
const diagnostics = new Array<Diagnostic>()
.concat(service.getSyntacticDiagnostics("/src.ts"))
.concat(service.getSemanticDiagnostics("/src.ts"))
.map(diagnostic => {
const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n");
if (diagnostic.file != null) {
let file = diagnostic.file.fileName.substring(1);
if (file === "src.ts") file = filename;
if (diagnostic.start == null) return file;
const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
}
else return message;
});
if (diagnostics.length > 0) {
throw new SyntaxError(diagnostics.join("\n"));
}
const outputs: Record<string, string> = {};
for (const el of emit.outputFiles) {
outputs[el.name] = el.text;
}
const rawMap = JSON.parse(outputs["/src.js.map"]);
const map = SourceMap.parse({
file: "ts-internal://" + filename,
mappings: rawMap.mappings,
sources: [filename],
});
const result = outputs["/src.js"];
const declaration = outputs["/src.d.ts"];
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
registerSource(filename, code);
return function (this: any) {
const res = compiled.apply(this, arguments);
if (declaration !== '') files["/src." + declI++ + ".d.ts"] = ScriptSnapshot.fromString(declaration);
return res;
};
};
}

View File

@@ -0,0 +1,16 @@
package me.topchetoeu.jscript.common;
public class FunctionBody {
public final FunctionBody[] children;
public final Instruction[] instructions;
public final int localsN, capturablesN, capturesN, length;
public FunctionBody(int localsN, int capturablesN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
this.children = children;
this.length = length;
this.localsN = localsN;
this.capturablesN = capturablesN;
this.capturesN = capturesN;
this.instructions = instructions;
}
}

View File

@@ -0,0 +1,376 @@
package me.topchetoeu.jscript.common;
import java.util.HashMap;
import java.util.function.IntFunction;
import java.util.function.IntSupplier;
import me.topchetoeu.jscript.common.parsing.Location;
public class Instruction {
public static enum Type {
RETURN(0x00),
NOP(0x01),
THROW(0x02),
THROW_SYNTAX(0x03),
DELETE(0x04),
TRY_START(0x05),
TRY_END(0x06),
CALL(0x10),
CALL_NEW(0x12),
JMP_IF(0x18),
JMP_IFN(0x19),
JMP(0x1A),
PUSH_UNDEFINED(0x20),
PUSH_NULL(0x21),
PUSH_BOOL(0x22),
PUSH_NUMBER(0x23),
PUSH_STRING(0x24),
DUP(0x25),
DISCARD(0x26),
LOAD_FUNC(0x30),
LOAD_ARR(0x31),
LOAD_OBJ(0x32),
LOAD_REGEX(0x33),
LOAD_GLOB(0x38),
LOAD_INTRINSICS(0x39),
LOAD_ARG(0x3A),
LOAD_ARGS_N(0x3B),
LOAD_ARGS(0x3C),
LOAD_CALLED(0x3D),
LOAD_THIS(0x3E),
LOAD_ERROR(0x3F),
LOAD_VAR(0x40),
LOAD_MEMBER(0x41),
LOAD_MEMBER_INT(0x42),
LOAD_MEMBER_STR(0x43),
STORE_VAR(0x48),
STORE_MEMBER(0x49),
STORE_MEMBER_INT(0x4A),
STORE_MEMBER_STR(0x4B),
DEF_PROP(0x50),
DEF_FIELD(0x51),
KEYS(0x52),
TYPEOF(0x53),
OPERATION(0x54),
GLOB_GET(0x60),
GLOB_SET(0x61),
GLOB_DEF(0x62);
private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric;
static {
for (var val : Type.values()) types.put(val.numeric, val);
}
private Type(int numeric) {
this.numeric = numeric;
}
public static Type fromNumeric(int i) {
return types.get(i);
}
}
public static enum BreakpointType {
/**
* A debugger should never stop at such instruction, unless a breakpoint has been set on it
*/
NONE,
/**
* Debuggers should pause at instructions marked with this breakpoint type
* after any step command
*/
STEP_OVER,
/**
* Debuggers should pause at instructions marked with this breakpoint type
* only after a step-in command
*/
STEP_IN;
public boolean shouldStepIn() {
return this != NONE;
}
public boolean shouldStepOver() {
return this == STEP_OVER;
}
}
public final Type type;
public final Object[] params;
@SuppressWarnings("unchecked")
public <T> T get(int i) {
if (i >= params.length || i < 0) return null;
return (T)params[i];
}
private Instruction(Type type, Object ...params) {
this.type = type;
this.params = params;
}
/**
* Signals the start of a protected context
* @param catchStart The point to witch to jump if an error has been caught
* @param finallyStart The point to witch to jump after either the try or catch bodies have exited
* @param end The point to which to jump after exiting the whole protected context
*/
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
}
/**
* Signifies that the current protected section (try, catch or finally) has ended
*/
public static Instruction tryEnd() {
return new Instruction(Type.TRY_END);
}
/**
* Throws the top stack value
*/
public static Instruction throwInstr() {
return new Instruction(Type.THROW);
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(SyntaxException err) {
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(String err) {
return new Instruction(Type.THROW_SYNTAX, err);
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(Location loc, String err) {
return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage());
}
/**
* Performs a JS object property deletion.
* Operands:
* 1. Object to manipulate
* 2. Key to delete
*/
public static Instruction delete() {
return new Instruction(Type.DELETE);
}
/**
* Returns the top stack value
*/
public static Instruction ret() {
return new Instruction(Type.RETURN);
}
/**
* A special NOP instruction telling any debugger to pause
*/
public static Instruction debug() {
return new Instruction(Type.NOP, "debug");
}
/**
* Does nothing. May be used for metadata or implementation-specific instructions that don't alter the behavior
*/
public static Instruction nop(Object ...params) {
return new Instruction(Type.NOP, params);
}
public static Instruction call(int argn, boolean hasSelf) {
return new Instruction(Type.CALL, argn, hasSelf);
}
public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, argn);
}
public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset);
}
public static Instruction jmpIf(int offset) {
return new Instruction(Type.JMP_IF, offset);
}
public static Instruction jmpIfNot(int offset) {
return new Instruction(Type.JMP_IFN, offset);
}
public static IntFunction<Instruction> jmp(IntSupplier pos) {
return i -> new Instruction(Type.JMP, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIf(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIfNot(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
}
public static Instruction pushUndefined() {
return new Instruction(Type.PUSH_UNDEFINED);
}
public static Instruction pushNull() {
return new Instruction(Type.PUSH_NULL);
}
public static Instruction pushValue(boolean val) {
return new Instruction(Type.PUSH_BOOL, val);
}
public static Instruction pushValue(double val) {
return new Instruction(Type.PUSH_NUMBER, val);
}
public static Instruction pushValue(String val) {
return new Instruction(Type.PUSH_STRING, val);
}
public static Instruction globDef(String name) {
return new Instruction(Type.GLOB_DEF, name);
}
public static Instruction globGet(String name, boolean force) {
return new Instruction(Type.GLOB_GET, name, force);
}
public static Instruction globSet(String name, boolean keep, boolean define) {
return new Instruction(Type.GLOB_SET, name, keep, define);
}
public static Instruction loadVar(int i) {
return new Instruction(Type.LOAD_VAR, i);
}
public static Instruction loadThis() {
return new Instruction(Type.LOAD_THIS);
}
/**
* Loads the given argument
* @param i The index of the argument to load. If -1, will get the index from the stack instead
*/
public static Instruction loadArg(int i) {
return new Instruction(Type.LOAD_ARG, i);
}
/**
* Pushes the amount of arguments to the stack
*/
public static Instruction loadArgsN() {
return new Instruction(Type.LOAD_ARGS_N);
}
/**
* Pushes the arguments object to the stack
*/
public static Instruction loadArgs() {
return new Instruction(Type.LOAD_ARGS);
}
/**
* Loads a reference to the function being called
*/
public static Instruction loadCalled() {
return new Instruction(Type.LOAD_CALLED);
}
public static Instruction loadGlob() {
return new Instruction(Type.LOAD_GLOB);
}
public static Instruction loadIntrinsics(String key) {
return new Instruction(Type.LOAD_INTRINSICS, key);
}
public static Instruction loadError() {
return new Instruction(Type.LOAD_ERROR);
}
public static Instruction loadMember() {
return new Instruction(Type.LOAD_MEMBER);
}
public static Instruction loadMember(int member) {
return new Instruction(Type.LOAD_MEMBER_INT, member);
}
public static Instruction loadMember(String member) {
return new Instruction(Type.LOAD_MEMBER_STR, member);
}
public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, flags);
}
// TODO: make this capturing a concern of the compiler
public static Instruction loadFunc(int id, String name, int[] captures) {
var args = new Object[2 + captures.length];
args[0] = id;
args[1] = name;
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
return new Instruction(Type.LOAD_FUNC, args);
}
public static Instruction loadObj() {
return new Instruction(Type.LOAD_OBJ);
}
public static Instruction loadArr(int count) {
return new Instruction(Type.LOAD_ARR, count);
}
public static Instruction dup() {
return new Instruction(Type.DUP, 1, 0);
}
public static Instruction dup(int count, int offset) {
return new Instruction(Type.DUP, count, offset);
}
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
return new Instruction(Type.STORE_VAR, i, keep, initialize);
}
public static Instruction storeMember() {
return new Instruction(Type.STORE_MEMBER, false);
}
public static Instruction storeMember(boolean keep) {
return new Instruction(Type.STORE_MEMBER, keep);
}
public static Instruction storeMember(String key) {
return new Instruction(Type.STORE_MEMBER_STR, key, false);
}
public static Instruction storeMember(String key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
}
public static Instruction storeMember(int key) {
return new Instruction(Type.STORE_MEMBER_INT, key, false);
}
public static Instruction storeMember(int key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_INT, key, keep);
}
public static Instruction discard() {
return new Instruction(Type.DISCARD);
}
public static Instruction typeof() {
return new Instruction(Type.TYPEOF);
}
public static Instruction typeof(String varName) {
return new Instruction(Type.TYPEOF, varName);
}
public static Instruction keys(boolean own, boolean onlyEnumerable) {
return new Instruction(Type.KEYS, own, onlyEnumerable);
}
public static Instruction defProp(boolean setter) {
return new Instruction(Type.DEF_PROP, setter);
}
public static Instruction defField() {
return new Instruction(Type.DEF_FIELD);
}
public static Instruction operation(Operation op) {
return new Instruction(Type.OPERATION, op);
}
@Override public String toString() {
var res = type.toString();
for (int i = 0; i < params.length; i++) {
res += " " + params[i];
}
return res;
}
}

View File

@@ -0,0 +1,29 @@
package me.topchetoeu.jscript.common;
import me.topchetoeu.jscript.common.json.JSON;
public class Metadata {
private static final String VERSION;
private static final String AUTHOR;
private static final String NAME;
static {
var data = JSON.parse(null, Reading.resourceToString("metadata.json")).map();
VERSION = data.string("version");
AUTHOR = data.string("author");
NAME = data.string("name");
}
public static String version() {
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
else return VERSION;
}
public static String author() {
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
else return AUTHOR;
}
public static String name() {
if (NAME.equals("$" + "{NAME}")) return "some-product";
else return NAME;
}
}

View File

@@ -0,0 +1,54 @@
package me.topchetoeu.jscript.common;
import java.util.HashMap;
public enum Operation {
INSTANCEOF(1, 2),
IN(2, 2),
MULTIPLY(3, 2),
DIVIDE(4, 2),
MODULO(5, 2),
ADD(6, 2),
SUBTRACT(7, 2),
USHIFT_RIGHT(8, 2),
SHIFT_RIGHT(9, 2),
SHIFT_LEFT(10, 2),
GREATER(11, 2),
LESS(12, 2),
GREATER_EQUALS(13, 2),
LESS_EQUALS(14, 2),
LOOSE_EQUALS(15, 2),
LOOSE_NOT_EQUALS(16, 2),
EQUALS(17, 2),
NOT_EQUALS(18, 2),
AND(19, 2),
OR(20, 2),
XOR(21, 2),
NEG(23, 1),
POS(24, 1),
NOT(25, 1),
INVERSE(26, 1);
private static final HashMap<Integer, Operation> operations = new HashMap<>();
static {
for (var val : Operation.values()) operations.put(val.numeric, val);
}
public final int numeric;
public final int operands;
private Operation(int numeric, int n) {
this.numeric = numeric;
this.operands = n;
}
public static Operation fromNumeric(int i) {
return operations.get(i);
}
}

View File

@@ -0,0 +1,92 @@
package me.topchetoeu.jscript.common;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Reading {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static synchronized String readline() throws IOException {
return reader.readLine();
}
public static byte[] streamToBytes(InputStream in) {
if (in == null) return null;
try {
List<byte[]> bufs = null;
byte[] result = null;
int total = 0;
int n;
do {
var buf = new byte[8192];
int nread = 0;
// read to EOF which may read more or less than buffer size
while ((n = in.read(buf, nread, buf.length - nread)) > 0) {
nread += n;
}
if (nread > 0) {
if (Integer.MAX_VALUE - 8 - total < nread) throw new OutOfMemoryError("Required array size too large");
if (nread < buf.length) buf = Arrays.copyOfRange(buf, 0, nread);
total += nread;
if (result == null) result = buf;
else {
if (bufs == null) {
bufs = new ArrayList<>();
bufs.add(result);
}
bufs.add(buf);
}
}
// if the last call to read returned -1 or the number of bytes
// requested have been read then break
} while (n >= 0);
if (bufs == null) {
if (result == null) return new byte[0];
return result.length == total ? result : Arrays.copyOf(result, total);
}
result = new byte[total];
int offset = 0;
int remaining = total;
for (byte[] b : bufs) {
int count = Math.min(b.length, remaining);
System.arraycopy(b, 0, result, offset, count);
offset += count;
remaining -= count;
}
return result;
}
catch (IOException e) { throw new UncheckedIOException(e); }
}
public static String streamToString(InputStream in) {
var bytes = streamToBytes(in);
if (bytes == null) return null;
else return new String(bytes);
}
public static InputStream resourceToStream(String name) {
return Reading.class.getResourceAsStream("/" + name);
}
public static String resourceToString(String name) {
return streamToString(resourceToStream(name));
}
public static byte[] resourceToBytes(String name) {
return streamToBytes(resourceToStream(name));
}
}

View File

@@ -0,0 +1,14 @@
package me.topchetoeu.jscript.common;
import me.topchetoeu.jscript.common.parsing.Location;
public class SyntaxException extends RuntimeException {
public final Location loc;
public final String msg;
public SyntaxException(Location loc, String msg) {
super(String.format("Syntax error %s: %s", loc, msg));
this.loc = loc;
this.msg = msg;
}
}

View File

@@ -0,0 +1,97 @@
package me.topchetoeu.jscript.common.environment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
public class Environment {
public final Environment parent;
private final Map<Key<Object>, Object> map = new HashMap<>();
private final Set<Key<Object>> hidden = new HashSet<>();
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
if (map.containsKey(key)) return (T)map.get(key);
else if (!hidden.contains(key) && parent != null) return parent.get(key);
else return null;
}
public boolean has(Key<?> key) {
if (map.containsKey(key)) return true;
else if (!hidden.contains(key) && parent != null) return parent.has(key);
else return false;
}
public boolean hasNotNull(Key<?> key) {
return get(key) != null;
}
public <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
public <T> T getWith(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key);
else return defaultVal.get();
}
@SuppressWarnings("unchecked")
public <T> Environment add(Key<T> key, T val) {
map.put((Key<Object>)key, val);
hidden.remove(key);
return this;
}
public Environment add(Key<Void> key) {
return add(key, null);
}
@SuppressWarnings("all")
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
map.putAll((Map)map);
hidden.removeAll(map.keySet());
return this;
}
public Environment addAll(Map<Key<?>, ?> map) {
return addAll(map, true);
}
@SuppressWarnings("unchecked")
public Environment remove(Key<?> key) {
map.remove(key);
hidden.add((Key<Object>)key);
return this;
}
public <T> T init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return val;
}
public <T> T initFrom(Key<T> key, Supplier<T> val) {
if (!has(key)) {
var res = val.get();
this.add(key, res);
return res;
}
else return get(key);
}
public Environment child() {
return new Environment(this);
}
public Environment(Environment parent) {
this.parent = parent;
}
public Environment() {
this.parent = null;
}
public static Environment wrap(Environment env) {
if (env == null) return empty();
else return env;
}
public static Environment empty() {
return new Environment();
}
}

View File

@@ -0,0 +1,3 @@
package me.topchetoeu.jscript.common.environment;
public final class Key<T> { }

View File

@@ -0,0 +1,173 @@
package me.topchetoeu.jscript.common.json;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.SyntaxException;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
public class JSON {
public static ParseRes<JSONElement> parseString(Source src, int i) {
var res = Parsing.parseString(src, i);
if (!res.isSuccess()) return res.chainError();
return ParseRes.res(JSONElement.string(res.result), res.n);
}
public static ParseRes<JSONElement> parseNumber(Source src, int i) {
var res = Parsing.parseNumber(src, i, true);
if (!res.isSuccess()) return res.chainError();
else return ParseRes.res(JSONElement.number(res.result), res.n);
}
public static ParseRes<JSONElement> parseLiteral(Source src, int i) {
var id = Parsing.parseIdentifier(src, i);
if (!id.isSuccess()) return ParseRes.failed();
else if (id.result.equals("true")) return ParseRes.res(JSONElement.bool(true), id.n);
else if (id.result.equals("false")) return ParseRes.res(JSONElement.bool(false), id.n);
else if (id.result.equals("null")) return ParseRes.res(JSONElement.NULL, id.n);
else return ParseRes.failed();
}
public static ParseRes<JSONElement> parseValue(Source src, int i) {
return ParseRes.first(src, i,
JSON::parseString,
JSON::parseNumber,
JSON::parseLiteral,
JSON::parseMap,
JSON::parseList
);
}
public static ParseRes<JSONElement> parseMap(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
var values = new JSONMap();
if (src.is(i + n, "}")) return ParseRes.res(JSONElement.map(new JSONMap(new HashMap<>())), n + 1);
while (true) {
var name = parseString(src, i + n);
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return name.chainError(src.loc(i + n), "Expected a colon");
n++;
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.put(name.result.toString(), res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "}")) {
n++;
break;
}
}
return ParseRes.res(JSONElement.map(values), n);
}
public static ParseRes<JSONElement> parseList(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n++, "[")) return ParseRes.failed();
var values = new JSONList();
if (src.is(i + n, "]")) return ParseRes.res(JSONElement.list(new JSONList()), n + 1);
while (true) {
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.add(res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "]")) {
n++;
break;
}
}
return ParseRes.res(JSONElement.list(values), n);
}
public static JSONElement parse(Filename filename, String raw) {
if (filename == null) filename = new Filename("jscript", "json");
var res = parseValue(new Source(null, filename, raw), 0);
if (res.isFailed()) throw new SyntaxException(Location.of(filename, 0, 0), "Invalid JSON given");
else if (res.isError()) throw new SyntaxException(res.errorLocation, res.error);
else return JSONElement.of(res.result);
}
public static String stringify(JSONElement el) {
if (el.isNumber()) {
var d = el.number();
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (el.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null";
if (el.isString()) {
var res = new StringBuilder("\"");
var alphabet = "0123456789ABCDEF".toCharArray();
for (var c : el.string().toCharArray()) {
if (c < 32 || c >= 127) {
res
.append("\\u")
.append(alphabet[(c >> 12) & 0xF])
.append(alphabet[(c >> 8) & 0xF])
.append(alphabet[(c >> 4) & 0xF])
.append(alphabet[(c >> 0) & 0xF]);
}
else if (c == '\\')
res.append("\\\\");
else if (c == '"')
res.append("\\\"");
else res.append(c);
}
return res.append('"').toString();
}
if (el.isList()) {
var res = new StringBuilder().append("[");
for (int i = 0; i < el.list().size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(el.list().get(i)));
}
res.append("]");
return res.toString();
}
if (el.isMap()) {
var res = new StringBuilder().append("{");
var entries = el.map().entrySet().stream().collect(Collectors.toList());
for (int i = 0; i < entries.size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
res.append(":");
res.append(stringify(entries.get(i).getValue()));
}
res.append("}");
return res.toString();
}
return null;
}
public static String stringify(JSONMap map) {
return stringify(JSONElement.of(map));
}
public static String stringify(JSONList list) {
return stringify(JSONElement.of(list));
}
}

View File

@@ -0,0 +1,88 @@
package me.topchetoeu.jscript.common.json;
public class JSONElement {
public static enum Type {
STRING,
NUMBER,
BOOLEAN,
NULL,
LIST,
MAP,
}
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
public static JSONElement map(JSONMap val) {
return new JSONElement(Type.MAP, val);
}
public static JSONElement list(JSONList val) {
return new JSONElement(Type.LIST, val);
}
public static JSONElement string(String val) {
return new JSONElement(Type.STRING, val);
}
public static JSONElement number(double val) {
return new JSONElement(Type.NUMBER, val);
}
public static JSONElement bool(boolean val) {
return new JSONElement(Type.BOOLEAN, val);
}
public static JSONElement of(Object val) {
if (val instanceof JSONElement el) return el;
else if (val instanceof JSONMap map) return map(map);
else if (val instanceof JSONList list) return list(list);
else if (val instanceof String str) return string(str);
else if (val instanceof Boolean bool) return bool(bool);
else if (val instanceof Number num) return number(num.doubleValue());
else if (val == null) return NULL;
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap");
}
public final Type type;
private final Object value;
public boolean isMap() { return type == Type.MAP; }
public boolean isList() { return type == Type.LIST; }
public boolean isString() { return type == Type.STRING; }
public boolean isNumber() { return type == Type.NUMBER; }
public boolean isBoolean() { return type == Type.BOOLEAN; }
public boolean isNull() { return type == Type.NULL; }
public JSONMap map() {
if (!isMap()) throw new IllegalStateException("Element is not a map");
return (JSONMap)value;
}
public JSONList list() {
if (!isList()) throw new IllegalStateException("Element is not a map");
return (JSONList)value;
}
public String string() {
if (!isString()) throw new IllegalStateException("Element is not a string");
return (String)value;
}
public double number() {
if (!isNumber()) throw new IllegalStateException("Element is not a number");
return (double)value;
}
public boolean bool() {
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean");
return (boolean)value;
}
@Override public String toString() {
if (isMap()) return "{...}";
if (isList()) return "[...]";
if (isString()) return (String)value;
if (isString()) return (String)value;
if (isNumber()) return (double)value + "";
if (isBoolean()) return (boolean)value + "";
if (isNull()) return "null";
return "";
}
private JSONElement(Type type, Object val) {
this.type = type;
this.value = val;
}
}

View File

@@ -0,0 +1,26 @@
package me.topchetoeu.jscript.common.json;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
public class JSONList extends ArrayList<JSONElement> {
public JSONList() {}
public JSONList(JSONElement ...els) {
super(Arrays.asList(els));
}
public JSONList(Collection<JSONElement> els) {
super(els);
}
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
}

View File

@@ -0,0 +1,136 @@
package me.topchetoeu.jscript.common.json;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class JSONMap implements Map<String, JSONElement> {
private Map<String, JSONElement> elements = new HashMap<>();
public JSONElement get(String path) {
JSONElement curr = JSONElement.map(this);
for (var seg : path.split("\\.")) {
if (!curr.isMap()) return null;
curr = curr.map().elements.get(seg);
}
return curr;
}
public boolean isMap(String path) {
var el = get(path);
return el != null && el.isMap();
}
public boolean isList(String path) {
var el = get(path);
return el != null && el.isList();
}
public boolean isString(String path) {
var el = get(path);
return el != null && el.isString();
}
public boolean isNumber(String path) {
var el = get(path);
return el != null && el.isNumber();
}
public boolean isBoolean(String path) {
var el = get(path);
return el != null && el.isBoolean();
}
public boolean isNull(String path) {
var el = get(path);
return el != null && el.isNull();
}
public boolean contains(String path) {
return get(path) != null;
}
public JSONMap map(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.map();
}
public JSONMap map(String path, JSONMap defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isMap()) return el.map();
return defaultVal;
}
public JSONList list(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.list();
}
public JSONList list(String path, JSONList defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isList()) return el.list();
return defaultVal;
}
public String string(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.string();
}
public String string(String path, String defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isString()) return el.string();
return defaultVal;
}
public double number(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.number();
}
public double number(String path, double defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isNumber()) return el.number();
return defaultVal;
}
public boolean bool(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.bool();
}
public boolean bool(String path, boolean defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isBoolean()) return el.bool();
return defaultVal;
}
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
@Override public int size() { return elements.size(); }
@Override public boolean isEmpty() { return elements.isEmpty(); }
@Override public boolean containsKey(Object key) { return elements.containsKey(key); }
@Override public boolean containsValue(Object value) { return elements.containsValue(value); }
@Override public JSONElement get(Object key) { return elements.get(key); }
@Override public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
@Override public JSONElement remove(Object key) { return elements.remove(key); }
@Override public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
@Override public void clear() { elements.clear(); }
@Override public Set<String> keySet() { return elements.keySet(); }
@Override public Collection<JSONElement> values() { return elements.values(); }
@Override public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
public JSONMap() { }
public JSONMap(Map<String, JSONElement> els) {
this.elements = new HashMap<>(els);
}
}

View File

@@ -0,0 +1,193 @@
package me.topchetoeu.jscript.common.mapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.Location;
public class FunctionMap {
public static class FunctionMapBuilder {
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
public Location toLocation(int pc) {
return sourceMap.headMap(pc, true).firstEntry().getValue();
}
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
if (loc == null || type == null || type == BreakpointType.NONE) return this;
breakpoints.put(loc, type);
return this;
}
public FunctionMapBuilder setLocation(int i, Location loc) {
if (loc == null || i < 0) return this;
sourceMap.put(i, loc);
return this;
}
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
setDebug(loc, type);
setLocation(i, loc);
return this;
}
public Location first() {
if (sourceMap.size() == 0) return null;
return sourceMap.firstEntry().getValue();
}
public Location last() {
if (sourceMap.size() == 0) return null;
return sourceMap.lastEntry().getValue();
}
public FunctionMapBuilder map(Function<Location, Location> mapper) {
var newSourceMaps = new HashMap<Integer, Location>();
var newBreakpoints = new HashMap<Location, BreakpointType>();
for (var key : sourceMap.keySet()) {
var mapped = mapper.apply(sourceMap.get(key));
if (mapped == null) continue;
newSourceMaps.put(key, mapped);
}
for (var key : breakpoints.keySet()) {
var mapped = mapper.apply(key);
if (mapped == null) continue;
newBreakpoints.put(mapped, breakpoints.get(key));
}
sourceMap.clear();
sourceMap.putAll(newSourceMaps);
breakpoints.clear();
breakpoints.putAll(newBreakpoints);
return this;
}
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
return new FunctionMap(sourceMap, breakpoints, localNames, capturableNames, captureNames);
}
public FunctionMap build(Function<Location, Location> mapper) {
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0], new String[0]);
}
private FunctionMapBuilder() { }
}
public static final FunctionMap EMPTY = new FunctionMap();
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
public final String[] localNames, capturableNames, captureNames;
public Location toLocation(int pc, boolean approxiamte) {
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
var res = pcToLoc.get(pc);
if (!approxiamte || res != null) return res;
var entry = pcToLoc.headMap(pc, true).lastEntry();
if (entry == null) return null;
else return entry.getValue();
}
public Location toLocation(int pc) {
return toLocation(pc, false);
}
public BreakpointType getBreakpoint(int pc) {
return bps.getOrDefault(pc, BreakpointType.NONE);
}
public Location correctBreakpoint(Location loc) {
var set = bpLocs.get(loc.filename());
if (set == null) return null;
else return set.ceiling(loc);
}
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
var candidates = new HashMap<Filename, TreeSet<Location>>();
for (var name : bpLocs.keySet()) {
if (filename.matcher(name.toString()).matches()) {
candidates.put(name, bpLocs.get(name));
}
}
var res = new ArrayList<Location>(candidates.size());
for (var candidate : candidates.entrySet()) {
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
if (val == null) continue;
res.add(val);
}
return res;
}
public List<Location> breakpoints(Location start, Location end) {
if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList();
NavigableSet<Location> set = bpLocs.get(start.filename());
if (set == null) return Arrays.asList();
if (start != null) set = set.tailSet(start, true);
if (end != null) set = set.headSet(end, true);
return set.stream().collect(Collectors.toList());
}
public Location start() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.firstEntry().getValue();
}
public Location end() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.lastEntry().getValue();
}
public FunctionMap clone() {
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), localNames, capturableNames, captureNames);
res.pcToLoc.putAll(this.pcToLoc);
res.bps.putAll(bps);
res.bpLocs.putAll(bpLocs);
res.pcToLoc.putAll(pcToLoc);
return res;
}
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] capturableNames, String[] captureNames) {
var locToPc = new HashMap<Location, Integer>();
for (var el : map.entrySet()) {
pcToLoc.put(el.getKey(), el.getValue());
locToPc.putIfAbsent(el.getValue(), el.getKey());
}
for (var el : breakpoints.entrySet()) {
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
bps.put(locToPc.get(el.getKey()), el.getValue());
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
bpLocs.get(el.getKey().filename()).add(el.getKey());
}
this.localNames = localNames;
this.captureNames = captureNames;
this.capturableNames = capturableNames;
}
private FunctionMap() {
localNames = new String[0];
captureNames = new String[0];
capturableNames = new String[0];
}
public static FunctionMapBuilder builder() {
return new FunctionMapBuilder();
}
}

View File

@@ -0,0 +1,53 @@
package me.topchetoeu.jscript.common.parsing;
import java.io.File;
public class Filename {
public final String protocol;
public final String path;
@Override public String toString() {
return protocol + "://" + path;
}
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + protocol.hashCode();
result = prime * result + path.hashCode();
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
var other = (Filename) obj;
if (protocol == null) {
if (other.protocol != null) return false;
}
else if (!protocol.equals(other.protocol)) return false;
if (path == null) {
if (other.path != null) return false;
}
else if (!path.equals(other.path)) return false;
return true;
}
public Filename(String protocol, String path) {
path = path.trim();
protocol = protocol.trim();
this.protocol = protocol;
this.path = path;
}
public static Filename parse(String val) {
var i = val.indexOf("://");
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
else return new Filename("file", val.trim());
}
public static Filename fromFile(File file) {
return new Filename("file", file.getAbsolutePath());
}
}

View File

@@ -0,0 +1,111 @@
package me.topchetoeu.jscript.common.parsing;
import java.util.ArrayList;
import java.util.Objects;
public abstract class Location implements Comparable<Location> {
public static final Location INTERNAL = Location.of(new Filename("jscript", "native"), -1, -1);
public abstract int line();
public abstract int start();
public abstract Filename filename();
public final String toString() {
var res = new ArrayList<String>();
if (filename() != null) res.add(filename().toString());
if (line() >= 0) res.add(line() + 1 + "");
if (start() >= 0) res.add(start() + 1 + "");
return String.join(":", res);
}
public final Location add(int n) {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return self.start() + n; }
@Override public int line() { return self.line(); }
};
}
public final Location nextLine() {
return changeLine(1);
}
public final Location changeLine(int offset) {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return 0; }
@Override public int line() { return self.line() + offset; }
};
}
@Override public int hashCode() {
return Objects.hash(line(), start(), filename());
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Location)) return false;
var other = (Location)obj;
if (!Objects.equals(this.start(), other.start())) return false;
if (!Objects.equals(this.line(), other.line())) return false;
if (!Objects.equals(this.filename(), other.filename())) return false;
return true;
}
@Override public int compareTo(Location other) {
int a = filename().toString().compareTo(other.filename().toString());
int b = Integer.compare(line(), other.line());
int c = Integer.compare(start(), other.start());
if (a != 0) return a;
if (b != 0) return b;
return c;
}
public static Location of(Filename filename, int line, int start) {
return new Location() {
@Override public Filename filename() { return filename; }
@Override public int start() { return start; }
@Override public int line() { return line; }
};
}
public static Location of(String raw) {
var i0 = raw.lastIndexOf(':');
if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1);
var i1 = raw.lastIndexOf(':', i0);
if (i0 < 0) {
try {
return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1);
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
}
int start, line;
try {
start = Integer.parseInt(raw.substring(i1 + 1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
try {
line = Integer.parseInt(raw.substring(i0 + 1, i1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1);
}
return Location.of(Filename.parse(raw.substring(0, i0)), start, line);
}
}

View File

@@ -0,0 +1,80 @@
package me.topchetoeu.jscript.common.parsing;
public class ParseRes<T> {
public static enum State {
SUCCESS,
FAILED,
ERROR;
public boolean isSuccess() { return this == SUCCESS; }
public boolean isFailed() { return this == FAILED; }
public boolean isError() { return this == ERROR; }
}
public final ParseRes.State state;
public final Location errorLocation;
public final String error;
public final T result;
public final int n;
private ParseRes(ParseRes.State state, Location errorLocation, String error, T result, int readN) {
this.result = result;
this.n = readN;
this.state = state;
this.error = error;
this.errorLocation = errorLocation;
}
public ParseRes<T> setN(int i) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, i);
}
public ParseRes<T> addN(int n) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, this.n + n);
}
public <T2> ParseRes<T2> chainError() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
return new ParseRes<>(state, errorLocation, error, null, 0);
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(ParseRes<?> other) {
if (!this.isError()) return other.chainError();
return (ParseRes<T2>) this;
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(Location loc, String error) {
if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0);
return (ParseRes<T2>) this;
}
public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); }
public boolean isError() { return state.isError(); }
public static <T> ParseRes<T> failed() {
return new ParseRes<T>(State.FAILED, null, null, null, 0);
}
public static <T> ParseRes<T> error(Location loc, String error) {
// TODO: differentiate definitive and probable errors
return new ParseRes<>(State.ERROR, loc, error, null, 0);
}
public static <T> ParseRes<T> res(T val, int i) {
return new ParseRes<>(State.SUCCESS, null, null, val, i);
}
@SafeVarargs
@SuppressWarnings("all")
public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) {
int n = Parsing.skipEmpty(src, i);
ParseRes<T> error = ParseRes.failed();
for (var parser : parsers) {
var res = parser.parse(src, i + n);
if (res.isSuccess()) return res.addN(n);
if (res.isError() && error.isFailed()) error = res.chainError();
}
return error;
}
}

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript.common.parsing;
public interface Parser<T> {
public ParseRes<T> parse(Source src, int i);
}

View File

@@ -0,0 +1,452 @@
package me.topchetoeu.jscript.common.parsing;
import me.topchetoeu.jscript.common.SyntaxException;
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
public class Parsing {
public static boolean isDigit(Character c) {
return c != null && c >= '0' && c <= '9';
}
public static boolean isAny(char c, String alphabet) {
return alphabet.contains(Character.toString(c));
}
public static int fromHex(char c) {
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= '0' && c <= '9') return c - '0';
return -1;
}
public static int skipEmpty(Source src, int i) {
return skipEmpty(src, i, true);
}
public static int skipEmpty(Source src, int i, boolean noComments) {
int n = 0;
if (i == 0 && src.is(0, "#!")) {
while (!src.is(n, '\n')) n++;
n++;
}
var isSingle = false;
var isMulti = false;
while (i + n < src.size()) {
if (isSingle) {
if (src.is(i + n, '\n')) {
n++;
isSingle = false;
}
else n++;
}
else if (isMulti) {
if (src.is(i + n, "*/")) {
n += 2;
isMulti = false;
}
else n++;
}
else if (src.is(i + n, "//")) {
n += 2;
isSingle = true;
}
else if (src.is(i + n, "/*")) {
n += 2;
isMulti = true;
}
else if (src.is(i + n, Character::isWhitespace)) {
n++;
}
else break;
}
return n;
}
public static ParseRes<Character> parseChar(Source src, int i) {
int n = 0;
if (src.is(i + n, '\\')) {
n++;
char c = src.at(i + n++);
if (c == 'b') return ParseRes.res('\b', n);
else if (c == 't') return ParseRes.res('\t', n);
else if (c == 'n') return ParseRes.res('\n', n);
else if (c == 'f') return ParseRes.res('\f', n);
else if (c == 'r') return ParseRes.res('\r', n);
else if (c == '0') {
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else return ParseRes.res('\0', n);
}
else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else if (c == 'x') {
var newC = 0;
for (var j = 0; j < 2; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == 'u') {
var newC = 0;
for (var j = 0; j < 4; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == '\n') return ParseRes.res(null, n);
else n--;
}
return ParseRes.res(src.at(i + n), n + 1);
}
public static ParseRes<String> parseIdentifier(Source src, int i) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else return ParseRes.res(res.toString(), n);
}
public static ParseRes<String> parseIdentifier(Source src, int i, String test) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n);
else return ParseRes.failed();
}
public static boolean isIdentifier(Source src, int i, String test) {
return parseIdentifier(src, i, test).isSuccess();
}
public static ParseRes<String> parseOperator(Source src, int i, String op) {
var n = skipEmpty(src, i);
if (src.is(i + n, op)) return ParseRes.res(op, n + op.length());
else return ParseRes.failed();
}
private static ParseRes<Double> parseHex(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = Parsing.fromHex(src.at(i + n, '\0'));
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 16;
res += digit;
}
}
private static ParseRes<Double> parseOct(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 8;
res += digit;
}
return ParseRes.res(res, n);
}
private static ParseRes<Double> parseBin(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 1) return ParseRes.error(src.loc(i + n), "Digits in binary literals must be from 0 to 1, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 2;
res += digit;
}
return ParseRes.res(res, n);
}
public static ParseRes<String> parseString(Source src, int i) {
var n = skipEmpty(src, i);
char quote;
if (src.is(i + n, '\'')) quote = '\'';
else if (src.is(i + n, '"')) quote = '"';
else return ParseRes.failed();
n++;
var res = new StringBuilder();
while (true) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal");
if (src.is(i + n, quote)) {
n++;
return ParseRes.res(res.toString(), n);
}
var charRes = parseChar(src, i + n);
if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character");
n += charRes.n;
if (charRes.result != null) res.append(charRes.result);
}
}
public static ParseRes<Double> parseNumber(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, "0o") || src.is(i + n, "0O")) {
n += 2;
var res = parseOct(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, "0b") || src.is(i + n, "0B")) {
n += 2;
var res = parseBin(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete binary literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, '0')) {
n++;
parsedAny = true;
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed");
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (parsedAny && src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
}
public static ParseRes<Double> parseFloat(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
parsedAny = true;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
}
public static ParseRes<Double> parseInt(Source src, int i, String alphabet, boolean withMinus) {
var n = skipEmpty(src, i);
double result = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (alphabet == null && src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
while (true) {
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n, '\0')));
if (digit < 0) break;
parsedAny = true;
result *= alphabet.length();
result += digit;
n++;
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-result, n);
else return ParseRes.res(result, n);
}
}

View File

@@ -0,0 +1,75 @@
package me.topchetoeu.jscript.common.parsing;
import java.util.function.Predicate;
import me.topchetoeu.jscript.common.environment.Environment;
public class Source {
public final Environment env;
public final Filename filename;
public final String src;
private int[] lineStarts;
public Location loc(int offset) {
return new SourceLocation(filename, lineStarts, offset);
}
public boolean is(int i, char c) {
return i >= 0 && i < src.length() && src.charAt(i) == c;
}
public boolean is(int i, String src) {
if (i < 0 || i + src.length() > size()) return false;
for (int j = 0; j < src.length(); j++) {
if (at(i + j) != src.charAt(j)) return false;
}
return true;
}
public boolean is(int i, Predicate<Character> predicate) {
if (i < 0 || i >= src.length()) return false;
return predicate.test(at(i));
}
public char at(int i) {
return src.charAt(i);
}
public char at(int i, char defaultVal) {
if (i < 0 || i >= src.length()) return defaultVal;
else return src.charAt(i);
}
public int size() {
return src.length();
}
public String slice(int start, int end) {
return src.substring(start, end);
}
public Source(Environment env, Filename filename, String src) {
if (env == null) this.env = new Environment();
else this.env = env;
this.filename = filename;
this.src = src;
int n = 1;
lineStarts = new int[16];
lineStarts[0] = 0;
for (int i = src.indexOf("\n"); i > 0; i = src.indexOf("\n", i + 1)) {
if (n >= lineStarts.length) {
var newArr = new int[lineStarts.length * 2];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
lineStarts[n++] = i + 1;
}
var newArr = new int[n];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
public Source(String src) {
this(null, null, src);
}
}

View File

@@ -0,0 +1,66 @@
package me.topchetoeu.jscript.common.parsing;
import java.util.Objects;
public class SourceLocation extends Location {
private int[] lineStarts;
private int line;
private int start;
private final Filename filename;
private final int offset;
private void update() {
if (lineStarts == null) return;
int a = 0;
int b = lineStarts.length;
while (true) {
if (a + 1 >= b) break;
var mid = -((-a - b) >> 1);
var el = lineStarts[mid];
if (el < offset) a = mid;
else if (el > offset) b = mid;
else {
this.line = mid;
this.start = 0;
this.lineStarts = null;
return;
}
}
this.line = a;
this.start = offset - lineStarts[a];
this.lineStarts = null;
return;
}
@Override public Filename filename() { return filename; }
@Override public int line() {
update();
return line;
}
@Override public int start() {
update();
return start;
}
@Override public int hashCode() {
return Objects.hash(offset);
}
@Override public int compareTo(Location other) {
if (other instanceof SourceLocation srcLoc) return Integer.compare(offset, srcLoc.offset);
else return super.compareTo(other);
}
@Override public boolean equals(Object obj) {
if (obj instanceof SourceLocation other) return this.offset == other.offset;
else return super.equals(obj);
}
public SourceLocation(Filename filename, int[] lineStarts, int offset) {
this.filename = filename;
this.lineStarts = lineStarts;
this.offset = offset;
}
}

View File

@@ -0,0 +1,141 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.control.TryNode;
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
import me.topchetoeu.jscript.compilation.scope.Variable;
public final class CompileResult {
public static final Key<Void> DEBUG_LOG = new Key<>();
public final List<Instruction> instructions;
public final List<CompileResult> children;
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
public final FunctionMapBuilder map;
public final Environment env;
public int length;
public final FunctionScope scope;
public final Map<TryNode, Variable> catchBindings = new HashMap<>();
public int temp() {
instructions.add(null);
return instructions.size() - 1;
}
public CompileResult add(Instruction instr) {
instructions.add(instr);
return this;
}
public CompileResult set(int i, Instruction instr) {
instructions.set(i, instr);
return this;
}
public int size() { return instructions.size(); }
public void setDebug(Location loc, BreakpointType type) {
map.setDebug(loc, type);
}
public void setLocation(int i, Location loc) {
map.setLocation(i, loc);
}
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
map.setLocationAndDebug(i, loc, type);
}
public void setDebug(BreakpointType type) {
setDebug(map.last(), type);
}
public void setLocation(Location type) {
setLocation(instructions.size() - 1, type);
}
public void setLocationAndDebug(Location loc, BreakpointType type) {
setLocationAndDebug(instructions.size() - 1, loc, type);
}
public CompileResult addChild(FunctionNode node, CompileResult res) {
this.children.add(res);
this.childrenMap.put(node, res);
this.childrenIndices.put(node, this.children.size() - 1);
return res;
}
public Instruction[] instructions() {
return instructions.toArray(new Instruction[0]);
}
public FunctionMap map(Function<Location, Location> mapper) {
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionMap map() {
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionBody body() {
var builtChildren = new FunctionBody[children.size()];
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
var instrRes = instructions();
if (env.has(DEBUG_LOG)) {
System.out.println("================= BODY =================");
System.out.println("LOCALS: " + scope.localsCount());
System.out.println("CAPTURABLES: " + scope.capturablesCount());
System.out.println("CAPTURES: " + scope.capturesCount());
for (var instr : instrRes) System.out.println(instr);
}
return new FunctionBody(
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren
);
}
public CompileResult subtarget() {
return new CompileResult(env, new FunctionScope(scope), this);
}
public CompileResult setEnvironment(Environment env) {
return new CompileResult(env, scope, this);
}
/**
* Returns a compile result with a child of the environment that relates to the given key.
* In essence, this is used to create a compile result which is back at the root environment of the compilation
*/
public CompileResult rootEnvironment(Key<Environment> env) {
return new CompileResult(this.env.get(env).child(), scope, this);
}
public CompileResult subEnvironment() {
return new CompileResult(env.child(), scope, this);
}
public CompileResult(Environment env, FunctionScope scope, int length) {
this.scope = scope;
this.instructions = new ArrayList<>();
this.children = new LinkedList<>();
this.map = FunctionMap.builder();
this.env = env;
this.length = length;
}
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
this.scope = scope;
this.instructions = parent.instructions;
this.children = parent.children;
this.map = parent.map;
this.env = env;
}
}

View File

@@ -0,0 +1,112 @@
package me.topchetoeu.jscript.compilation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
public class CompoundNode extends Node {
public final Node[] statements;
public Location end;
@Override public void resolve(CompileResult target) {
for (var stm : statements) stm.resolve(target);
}
@Override public void compileFunctions(CompileResult target) {
for (var stm : statements) stm.compileFunctions(target);
}
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
List<Node> statements = new ArrayList<Node>();
for (var stm : this.statements) {
if (stm instanceof FunctionStatementNode func) {
func.compile(target, false);
}
else statements.add(stm);
}
var polluted = false;
for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
}
if (!polluted && pollute) {
target.add(Instruction.pushUndefined());
}
}
public CompoundNode setEnd(Location loc) {
this.end = loc;
return this;
}
public CompoundNode(Location loc, Node ...statements) {
super(loc);
this.statements = statements;
}
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
if (precedence > 1) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, ",")) return ParseRes.failed();
n++;
var curr = JavaScript.parseExpression(src, i + n, 2);
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
n += curr.n;
if (prev instanceof CompoundNode comp) {
var children = new ArrayList<Node>();
children.addAll(Arrays.asList(comp.statements));
children.add(curr.result);
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
}
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
}
public static ParseRes<CompoundNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
var statements = new ArrayList<Node>();
while (true) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
if (src.is(i + n, ";")) {
n++;
continue;
}
var res = JavaScript.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
n += res.n;
statements.add(res.result);
}
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
}
}

Some files were not shown because too many files have changed in this diff Show More