├── .gitattributes ├── src ├── implementation.h ├── native.h ├── builtin │ ├── json.h │ ├── math.h │ ├── date.h │ ├── number.h │ ├── boolean.h │ ├── array.h │ ├── arguments.h │ ├── global.h │ ├── string.h │ ├── regexp.h │ ├── error.h │ ├── boolean.c │ ├── function.h │ ├── arguments.c │ ├── object.h │ ├── number.c │ ├── error.c │ ├── math.c │ ├── global.c │ ├── function.c │ └── json.c ├── parser.h ├── input.h ├── env.h ├── pool.h ├── compatibility.h ├── oplist.h ├── key.h ├── chars.h ├── ecc.h ├── interface.h ├── context.h ├── lexer.h ├── text.h ├── key.c ├── op.h ├── namespace.h ├── env.c ├── input.c ├── value.h ├── ecc.c ├── context.c ├── pool.c ├── chars.c └── oplist.c ├── .gitignore ├── LICENSE.txt └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | -------------------------------------------------------------------------------- /src/implementation.h: -------------------------------------------------------------------------------- 1 | // 2 | // implementation.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define io_libecc_interface_Implementation 10 | #include "interface.h" 11 | -------------------------------------------------------------------------------- /src/native.h: -------------------------------------------------------------------------------- 1 | // 2 | // native.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_native_h 10 | #define io_libecc_native_h 11 | 12 | #include "namespace.h" 13 | 14 | struct Value; 15 | struct Context; 16 | 17 | typedef struct Value io_libecc_interface_Unwrap ((* Native(Function))) (struct Context * const context); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build/*/bin/ 3 | build/*/lib/ 4 | build/*/object/ 5 | 6 | # OSX 7 | 8 | .DS_Store 9 | 10 | # Linux 11 | 12 | *~ 13 | .directory 14 | .Trash-* 15 | 16 | # Windows 17 | 18 | Thumbs.db 19 | 20 | # Xcode 21 | 22 | *.pbxuser 23 | !default.pbxuser 24 | *.mode1v3 25 | !default.mode1v3 26 | *.mode2v3 27 | !default.mode2v3 28 | *.perspectivev3 29 | !default.perspectivev3 30 | xcuserdata 31 | *.xccheckout 32 | *.moved-aside 33 | DerivedData 34 | *.xcuserstate 35 | -------------------------------------------------------------------------------- /src/builtin/json.h: -------------------------------------------------------------------------------- 1 | // 2 | // json.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef json_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define json_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * JSON(object); 21 | extern const struct Object(Type) JSON(type); 22 | 23 | #endif 24 | 25 | 26 | Interface(JSON, 27 | 28 | (void, setup ,(void)) 29 | (void, teardown ,(void)) 30 | , 31 | { 32 | char empty; 33 | } 34 | ) 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/builtin/math.h: -------------------------------------------------------------------------------- 1 | // 2 | // math.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_math_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_math_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Math(object); 21 | extern const struct Object(Type) Math(type); 22 | 23 | #endif 24 | 25 | 26 | Interface(Math, 27 | 28 | (void, setup ,(void)) 29 | (void, teardown ,(void)) 30 | , 31 | { 32 | char empty; 33 | } 34 | ) 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/builtin/date.h: -------------------------------------------------------------------------------- 1 | // 2 | // date.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_date_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_date_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Date(prototype); 21 | extern struct Function * Date(constructor); 22 | extern const struct Object(Type) Date(type); 23 | 24 | #endif 25 | 26 | 27 | Interface(Date, 28 | 29 | (void, setup ,(void)) 30 | (void, teardown ,(void)) 31 | 32 | (struct Date *, create ,(double)) 33 | , 34 | { 35 | struct Object object; 36 | double ms; 37 | } 38 | ) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/builtin/number.h: -------------------------------------------------------------------------------- 1 | // 2 | // number.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_number_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_number_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Number(prototype); 21 | extern struct Function * Number(constructor); 22 | extern const struct Object(Type) Number(type); 23 | 24 | #endif 25 | 26 | 27 | Interface(Number, 28 | 29 | (void, setup ,(void)) 30 | (void, teardown ,(void)) 31 | 32 | (struct Number *, create ,(double)) 33 | , 34 | { 35 | struct Object object; 36 | double value; 37 | } 38 | ) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/builtin/boolean.h: -------------------------------------------------------------------------------- 1 | // 2 | // boolean.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_boolean_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_boolean_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Boolean(prototype); 21 | extern struct Function * Boolean(constructor); 22 | extern const struct Object(Type) Boolean(type); 23 | 24 | #endif 25 | 26 | 27 | Interface(Boolean, 28 | 29 | (void, setup ,(void)) 30 | (void, teardown ,(void)) 31 | 32 | (struct Boolean *, create ,(int)) 33 | , 34 | { 35 | struct Object object; 36 | int truth; 37 | } 38 | ) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/builtin/array.h: -------------------------------------------------------------------------------- 1 | // 2 | // array.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_array_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_array_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Array(prototype); 21 | extern struct Function * Array(constructor); 22 | extern const struct Object(Type) Array(type); 23 | 24 | #endif 25 | 26 | 27 | Interface(Array, 28 | 29 | (void, setup ,(void)) 30 | (void, teardown ,(void)) 31 | 32 | (struct Object *, create ,(void)) 33 | (struct Object *, createSized ,(uint32_t size)) 34 | , 35 | { 36 | struct Object object; 37 | } 38 | ) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/builtin/arguments.h: -------------------------------------------------------------------------------- 1 | // 2 | // arguments.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_arguments_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_arguments_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Arguments(prototype); 21 | extern const struct Object(Type) Arguments(type); 22 | 23 | #endif 24 | 25 | 26 | Interface(Arguments, 27 | 28 | (void, setup ,(void)) 29 | (void, teardown ,(void)) 30 | 31 | (struct Object *, createSized ,(uint32_t size)) 32 | (struct Object *, createWithCList ,(int count, const char * list[])) 33 | , 34 | { 35 | struct Object object; 36 | } 37 | ) 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/builtin/global.h: -------------------------------------------------------------------------------- 1 | // 2 | // global.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc__global_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc__global_h 17 | 18 | #include "object.h" 19 | #include "array.h" 20 | #include "function.h" 21 | #include "boolean.h" 22 | #include "date.h" 23 | #include "arguments.h" 24 | #include "math.h" 25 | #include "number.h" 26 | #include "regexp.h" 27 | #include "error.h" 28 | #include "json.h" 29 | 30 | extern const struct Object(Type) Global(type); 31 | 32 | #endif 33 | 34 | 35 | Interface(Global, 36 | 37 | (void, setup ,(void)) 38 | (void, teardown ,(void)) 39 | 40 | (struct Function *, create ,(void)) 41 | , 42 | { 43 | char empty; 44 | } 45 | ) 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/builtin/string.h: -------------------------------------------------------------------------------- 1 | // 2 | // string.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_string_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_string_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * String(prototype); 21 | extern struct Function * String(constructor); 22 | extern const struct Object(Type) String(type); 23 | 24 | #endif 25 | 26 | 27 | Interface(String, 28 | 29 | (void, setup ,(void)) 30 | (void, teardown ,(void)) 31 | 32 | (struct String *, create ,(struct Chars *)) 33 | (struct Value, valueAtIndex ,(struct String *, int32_t index)) 34 | 35 | (struct Text, textAtIndex ,(const char *chars, int32_t length, int32_t index, int enableReverse)) 36 | (int32_t, unitIndex ,(const char *chars, int32_t max, int32_t unit)) 37 | , 38 | { 39 | struct Object object; 40 | struct Chars *value; 41 | } 42 | ) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // parser.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_parser_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_parser_h 17 | 18 | #include "lexer.h" 19 | 20 | #endif 21 | 22 | 23 | Interface(Parser, 24 | 25 | (struct Parser *, createWithLexer ,(struct Lexer *)) 26 | (void, destroy, (struct Parser *)) 27 | 28 | (struct Function *, parseWithEnvironment ,(struct Parser * const, struct Object *environment, struct Object *global)) 29 | , 30 | { 31 | struct Lexer *lexer; 32 | enum Lexer(Token) previewToken; 33 | struct Error *error; 34 | 35 | struct { 36 | struct Key key; 37 | char depth; 38 | } *depths; 39 | uint16_t depthCount; 40 | 41 | struct Object *global; 42 | struct Function *function; 43 | uint16_t sourceDepth; 44 | 45 | int preferInteger; 46 | int strictMode; 47 | int reserveGlobalSlots; 48 | } 49 | ) 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Aurélien Bouilland 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | // 2 | // input.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_input_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_input_h 17 | 18 | #include "value.h" 19 | #include "env.h" 20 | 21 | #endif 22 | 23 | 24 | Interface(Input, 25 | 26 | (struct Input *, createFromFile ,(const char *filename)) 27 | (struct Input *, createFromBytes ,(const char *bytes, uint32_t length, const char *name, ...)) 28 | (void, destroy ,(struct Input *)) 29 | 30 | (void, printText, (struct Input *, struct Text text, int32_t ofLine, struct Text ofText, const char *ofInput, int fullLine)) 31 | (int32_t, findLine, (struct Input *, struct Text text)) 32 | 33 | (struct Value, attachValue, (struct Input *, struct Value value)) 34 | , 35 | { 36 | char name[FILENAME_MAX]; 37 | 38 | uint32_t length; 39 | char *bytes; 40 | 41 | uint16_t lineCount; 42 | uint16_t lineCapacity; 43 | uint32_t *lines; 44 | 45 | struct Value *attached; 46 | uint16_t attachedCount; 47 | } 48 | ) 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/env.h: -------------------------------------------------------------------------------- 1 | // 2 | // env.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_env_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_env_h 17 | 18 | enum Env(Color) { 19 | Env(black) = 30, 20 | Env(red) = 31, 21 | Env(green) = 32, 22 | Env(yellow) = 33, 23 | Env(blue) = 34, 24 | Env(magenta) = 35, 25 | Env(cyan) = 36, 26 | Env(white) = 37, 27 | }; 28 | 29 | enum Env(Attribute) { 30 | Env(bold) = 1, 31 | Env(dim) = 2, 32 | Env(invisible) = 8, 33 | }; 34 | 35 | extern const int Env(print_max); 36 | 37 | #endif 38 | 39 | 40 | Interface(Env, 41 | 42 | (void, setup ,(void)) 43 | (void, teardown ,(void)) 44 | 45 | (void, print ,(const char *format, ...)) 46 | (void, printColor ,(enum Env(Color) color, enum Env(Attribute) attribute, const char *format, ...)) 47 | (void, printError ,(int typeLength, const char *type, const char *format, ...)) 48 | (void, printWarning ,(const char *format, ...)) 49 | (void, newline ,(void)) 50 | 51 | (double, currentTime ,(void)) 52 | , 53 | { 54 | struct Env(Internal) *internal; 55 | } 56 | ) 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/pool.h: -------------------------------------------------------------------------------- 1 | // 2 | // pool.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_pool_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_pool_h 17 | 18 | #include "builtin/function.h" 19 | 20 | #endif 21 | 22 | 23 | Interface(Pool, 24 | 25 | (void, setup ,(void)) 26 | (void, teardown ,(void)) 27 | 28 | (void, addFunction ,(struct Function *function)) 29 | (void, addObject ,(struct Object *object)) 30 | (void, addChars ,(struct Chars *chars)) 31 | 32 | (void, unmarkAll ,(void)) 33 | (void, markValue ,(struct Value value)) 34 | (void, markObject ,(struct Object *object)) 35 | 36 | (void, collectUnmarked ,(void)) 37 | (void, collectUnreferencedFromIndices ,(uint32_t indices[3])) 38 | (void, unreferenceFromIndices ,(uint32_t indices[3])) 39 | 40 | (void, getIndices ,(uint32_t indices[3])) 41 | , 42 | { 43 | struct Function **functionList; 44 | uint32_t functionCount; 45 | uint32_t functionCapacity; 46 | 47 | struct Object **objectList; 48 | uint32_t objectCount; 49 | uint32_t objectCapacity; 50 | 51 | struct Chars **charsList; 52 | uint32_t charsCount; 53 | uint32_t charsCapacity; 54 | } 55 | ) 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/builtin/regexp.h: -------------------------------------------------------------------------------- 1 | // 2 | // regexp.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_regexp_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_regexp_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * RegExp(prototype); 21 | extern struct Function * RegExp(constructor); 22 | extern const struct Object(Type) RegExp(type); 23 | 24 | struct RegExp(State) { 25 | const char * const start; 26 | const char * const end; 27 | const char **capture; 28 | const char **index; 29 | int flags; 30 | }; 31 | 32 | struct RegExp(Node); 33 | 34 | enum RegExp(Options) { 35 | RegExp(allowUnicodeFlags) = 1 << 0, 36 | }; 37 | 38 | #endif 39 | 40 | 41 | Interface(RegExp, 42 | 43 | (void, setup ,(void)) 44 | (void, teardown ,(void)) 45 | 46 | (struct RegExp *, create ,(struct Chars *pattern, struct Error **, enum RegExp(Options))) 47 | (struct RegExp *, createWith ,(struct Context *context, struct Value pattern, struct Value flags)) 48 | 49 | (int, matchWithState ,(struct RegExp *, struct RegExp(State) *)) 50 | , 51 | { 52 | struct Object object; 53 | struct Chars *pattern; 54 | struct Chars *source; 55 | struct RegExp(Node) *program; 56 | uint8_t count; 57 | uint8_t global:1; 58 | uint8_t ignoreCase:1; 59 | uint8_t multiline:1; 60 | } 61 | ) 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/compatibility.h: -------------------------------------------------------------------------------- 1 | // 2 | // compatibility.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef libecc_compatibility_h 10 | #define libecc_compatibility_h 11 | 12 | #if __STDC__ || _MSC_EXTENSIONS 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if __GNUC__ 31 | #define io_libecc_ecc_noreturn __attribute__((noreturn)) 32 | #else 33 | #define io_libecc_ecc_noreturn 34 | #endif 35 | 36 | #if __GNUC__ && _WIN32 && !_MSC_VER 37 | /* use ebp frame */ 38 | #define io_libecc_ecc_useframe __attribute__((optimize("no-omit-frame-pointer"))) 39 | #else 40 | #define io_libecc_ecc_useframe 41 | #endif 42 | 43 | #if (__STDC_VERSION__ < 199901L) 44 | #ifdef __GNUC__ 45 | #define inline __inline__ 46 | #define restrict __restrict__ 47 | #else 48 | #define inline static 49 | #define restrict 50 | #endif 51 | #endif 52 | 53 | #if (__unix__ && !__MSDOS__) || (defined(__APPLE__) && defined(__MACH__)) 54 | /* don't use signal version of long jump */ 55 | #undef setjmp 56 | #define setjmp _setjmp 57 | 58 | #undef longjmp 59 | #define longjmp _longjmp 60 | #endif 61 | 62 | #if _WIN32 63 | /* supports hex */ 64 | #define strtod strtold 65 | #endif 66 | 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/oplist.h: -------------------------------------------------------------------------------- 1 | // 2 | // oplist.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_oplist_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_oplist_h 17 | 18 | #include "op.h" 19 | 20 | #endif 21 | 22 | 23 | Interface(OpList, 24 | 25 | (struct OpList *, create ,(const Native(Function) native, struct Value value, struct Text text)) 26 | (void, destroy ,(struct OpList *)) 27 | 28 | (struct OpList *, join ,(struct OpList *, struct OpList *)) 29 | (struct OpList *, join3 ,(struct OpList *, struct OpList *, struct OpList *)) 30 | (struct OpList *, joinDiscarded ,(struct OpList *, uint16_t n, struct OpList *)) 31 | (struct OpList *, unshift ,(struct Op op, struct OpList *)) 32 | (struct OpList *, unshiftJoin ,(struct Op op, struct OpList *, struct OpList *)) 33 | (struct OpList *, unshiftJoin3 ,(struct Op op, struct OpList *, struct OpList *, struct OpList *)) 34 | (struct OpList *, shift ,(struct OpList *)) 35 | (struct OpList *, append ,(struct OpList *, struct Op op)) 36 | (struct OpList *, appendNoop ,(struct OpList *)) 37 | (struct OpList *, createLoop ,(struct OpList * initial, struct OpList * condition, struct OpList * step, struct OpList * body, int reverseCondition)) 38 | 39 | (void, optimizeWithEnvironment, (struct OpList *, struct Object *environment, uint32_t index)) 40 | 41 | (void, dumpTo ,(struct OpList *, FILE *file)) 42 | (struct Text, text ,(struct OpList *oplist)) 43 | , 44 | { 45 | uint32_t count; 46 | struct Op *ops; 47 | } 48 | ) 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/key.h: -------------------------------------------------------------------------------- 1 | // 2 | // key.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef monade_key_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define monade_key_h 17 | 18 | #include "text.h" 19 | 20 | extern struct Key Key(none); 21 | 22 | #define io_libecc_key_Keys \ 23 | _( prototype )\ 24 | _( constructor )\ 25 | _( length )\ 26 | _( arguments )\ 27 | _( callee )\ 28 | _( name )\ 29 | _( message )\ 30 | _( toString )\ 31 | _( valueOf )\ 32 | _( eval )\ 33 | _( value )\ 34 | _( writable )\ 35 | _( enumerable )\ 36 | _( configurable )\ 37 | _( get )\ 38 | _( set )\ 39 | _( join )\ 40 | _( toISOString )\ 41 | _( input )\ 42 | _( index )\ 43 | _( lastIndex )\ 44 | _( global )\ 45 | _( ignoreCase )\ 46 | _( multiline )\ 47 | _( source )\ 48 | \ 49 | 50 | #define _(X) extern struct Key Key(X); 51 | io_libecc_key_Keys 52 | #undef _ 53 | 54 | enum Key(Flags) { 55 | Key(copyOnCreate) = (1 << 0), 56 | }; 57 | 58 | #endif 59 | 60 | 61 | Interface(Key, 62 | 63 | (void, setup ,(void)) 64 | (void, teardown ,(void)) 65 | 66 | (struct Key, makeWithCString ,(const char *cString)) 67 | (struct Key, makeWithText ,(const struct Text text, enum Key(Flags) flags)) 68 | (struct Key, search ,(const struct Text text)) 69 | 70 | (int, isEqual, (struct Key, struct Key)) 71 | (const struct Text *, textOf, (struct Key)) 72 | 73 | (void, dumpTo, (struct Key, FILE *)) 74 | , 75 | { 76 | union { 77 | uint8_t depth[4]; 78 | uint32_t integer; 79 | } data; 80 | } 81 | ) 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /src/chars.h: -------------------------------------------------------------------------------- 1 | // 2 | // chars.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_chars_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_chars_h 17 | 18 | #include "builtin/string.h" 19 | 20 | typedef struct { 21 | uint32_t value:24; 22 | } Chars(length); 23 | 24 | enum Chars(Flags) 25 | { 26 | Chars(mark) = 1 << 0, 27 | Chars(asciiOnly) = 1 << 1, 28 | }; 29 | 30 | struct Chars(Append) { 31 | struct Chars *value; 32 | char buffer[9]; 33 | uint8_t units; 34 | }; 35 | 36 | #endif 37 | 38 | 39 | Interface(Chars, 40 | 41 | (struct Chars *, createVA ,(int32_t length, const char *format, va_list ap)) 42 | (struct Chars *, create ,(const char *format, ...)) 43 | (struct Chars *, createSized ,(int32_t length)) 44 | (struct Chars *, createWithBytes ,(int32_t length, const char *bytes)) 45 | 46 | (void, beginAppend ,(struct Chars(Append) *)) 47 | (void, append ,(struct Chars(Append) *, const char *format, ...)) 48 | (void, appendCodepoint ,(struct Chars(Append) *, uint32_t cp)) 49 | (void, appendValue ,(struct Chars(Append) *, struct Context * const context, struct Value value)) 50 | (void, appendBinary ,(struct Chars(Append) *, double binary, int base)) 51 | (void, normalizeBinary ,(struct Chars(Append) *)) 52 | (struct Value, endAppend ,(struct Chars(Append) *)) 53 | 54 | (void, destroy ,(struct Chars *)) 55 | 56 | (uint8_t, codepointLength ,(uint32_t cp)) 57 | (uint8_t, writeCodepoint ,(char *, uint32_t cp)) 58 | , 59 | { 60 | int32_t length; 61 | int16_t referenceCount; 62 | uint8_t flags; 63 | char bytes[1]; 64 | } 65 | ) 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | libecc 3 | ====== 4 | 5 | Fast, memory-efficient and easily embeddable Ecmascript (5.1) engine for C (99, GNU) 6 | 7 | | support | tested on | 8 | | ------- | ----------------------------- | 9 | | Darwin | macOS, iOS | 10 | | Linux | Ubuntu, Fedora, Raspberry Pi | 11 | | Windows | minGW, Visual Studio w/ Clang | 12 | | Dos | DJGPP | 13 | | BeOS | Haiku | 14 | 15 | Build 16 | ----- 17 | 18 | $ git clone https://github.com/blld/libecc.git 19 | $ cd libecc/build 20 | $ make test 21 | 22 | Usage 23 | ----- 24 | 25 | sample.c 26 | 27 | #include "ecc.h" 28 | 29 | static 30 | struct Value greetings (struct Context * context) 31 | { 32 | // retrieve first argument as string 33 | struct Value to = Value.toString(context, Context.argument(context, 0)); 34 | 35 | // get C-friendly buffer 36 | struct Text text = Value.textOf(&to); 37 | 38 | // print & return undefined 39 | printf("Hello, %.*s!\n", text.length, text.bytes); 40 | return Value(undefined); 41 | } 42 | 43 | int main (int argc, const char * argv[]) 44 | { 45 | // setup 46 | struct Ecc *ecc = Ecc.create(); 47 | 48 | // add C function 49 | Ecc.addFunction(ecc, "greetings", greetings, 1, 0); 50 | 51 | // run script 52 | char script1[] = "greetings('world')"; 53 | Ecc.evalInput(ecc, Input.createFromBytes(script1, sizeof(script1), "script1"), 0); 54 | 55 | // clean-up 56 | Ecc.destroy(ecc), ecc = NULL; 57 | return EXIT_SUCCESS; 58 | } 59 | 60 | compile 61 | 62 | $ cc -I ../src -L */lib -lecc sample.c 63 | $ ./a.out 64 | Hello, world! 65 | 66 | License 67 | ------- 68 | 69 | Licensed under MIT license, see LICENSE.txt file in project root 70 | 71 | -------------------------------------------------------------------------------- /src/builtin/error.h: -------------------------------------------------------------------------------- 1 | // 2 | // error.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_error_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_error_h 17 | 18 | #include "global.h" 19 | 20 | extern struct Object * Error(prototype); 21 | extern struct Object * Error(rangePrototype); 22 | extern struct Object * Error(referencePrototype); 23 | extern struct Object * Error(syntaxPrototype); 24 | extern struct Object * Error(typePrototype); 25 | extern struct Object * Error(uriPrototype); 26 | extern struct Object * Error(evalPrototype); 27 | 28 | extern struct Function * Error(constructor); 29 | extern struct Function * Error(rangeConstructor); 30 | extern struct Function * Error(referenceConstructor); 31 | extern struct Function * Error(syntaxConstructor); 32 | extern struct Function * Error(typeConstructor); 33 | extern struct Function * Error(uriConstructor); 34 | extern struct Function * Error(evalConstructor); 35 | 36 | extern const struct Object(Type) Error(type); 37 | 38 | #endif 39 | 40 | 41 | Interface(Error, 42 | 43 | (void, setup ,(void)) 44 | (void, teardown ,(void)) 45 | 46 | (struct Error *, error ,(struct Text, struct Chars *message)) 47 | (struct Error *, rangeError ,(struct Text, struct Chars *message)) 48 | (struct Error *, referenceError ,(struct Text, struct Chars *message)) 49 | (struct Error *, syntaxError ,(struct Text, struct Chars *message)) 50 | (struct Error *, typeError ,(struct Text, struct Chars *message)) 51 | (struct Error *, uriError ,(struct Text, struct Chars *message)) 52 | (void, destroy ,(struct Error *)) 53 | , 54 | { 55 | struct Object object; 56 | struct Text text; 57 | } 58 | ) 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/ecc.h: -------------------------------------------------------------------------------- 1 | // 2 | // ecc.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_ecc_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_ecc_h 17 | 18 | #include "builtin/global.h" 19 | #include "input.h" 20 | 21 | enum Ecc(EvalFlags) { 22 | Ecc(sloppyMode) = 0x1 /* 0000 0001 */, 23 | Ecc(primitiveResult) = 0x2 /* 0000 0010 */, 24 | Ecc(stringResult) = 0x6 /* 0000 0110 */, 25 | }; 26 | 27 | extern uint32_t Ecc(version); 28 | 29 | #endif 30 | 31 | 32 | Interface(Ecc, 33 | 34 | (struct Ecc *, create ,(void)) 35 | (void, destroy ,(struct Ecc *)) 36 | 37 | (void, addValue ,(struct Ecc *, const char *name, struct Value value, enum Value(Flags))) 38 | (void, addFunction ,(struct Ecc *, const char *name, const Native(Function) native, int argumentCount, enum Value(Flags))) 39 | 40 | (int, evalInput ,(struct Ecc *, struct Input *, enum Ecc(EvalFlags))) 41 | (void, evalInputWithContext ,(struct Ecc *, struct Input *, struct Context *context)) 42 | 43 | (jmp_buf *, pushEnv ,(struct Ecc *)) 44 | (void, popEnv ,(struct Ecc *)) 45 | (void, jmpEnv ,(struct Ecc *, struct Value value) Ecc(noreturn)) 46 | (void, fatal ,(const char *format, ...) Ecc(noreturn)) 47 | 48 | (struct Input *, findInput ,(struct Ecc *self, struct Text text)) 49 | (void, printTextInput ,(struct Ecc *, struct Text text, int fullLine)) 50 | 51 | (void, garbageCollect ,(struct Ecc *)) 52 | , 53 | { 54 | jmp_buf *envList; 55 | uint16_t envCount; 56 | uint16_t envCapacity; 57 | 58 | struct Function *global; 59 | 60 | struct Value result; 61 | struct Text text; 62 | int32_t ofLine; 63 | struct Text ofText; 64 | const char *ofInput; 65 | 66 | struct Input **inputs; 67 | uint16_t inputCount; 68 | 69 | int16_t maximumCallDepth; 70 | unsigned printLastThrow:1; 71 | unsigned sloppyMode:1; 72 | } 73 | ) 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/builtin/boolean.c: -------------------------------------------------------------------------------- 1 | // 2 | // boolean.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "boolean.h" 11 | 12 | #include "../pool.h" 13 | 14 | // MARK: - Private 15 | 16 | struct Object * Boolean(prototype) = NULL; 17 | struct Function * Boolean(constructor) = NULL; 18 | 19 | const struct Object(Type) Boolean(type) = { 20 | .text = &Text(booleanType), 21 | }; 22 | 23 | // MARK: - Static Members 24 | 25 | static 26 | struct Value toString (struct Context * const context) 27 | { 28 | int truth; 29 | 30 | Context.assertThisMask(context, Value(booleanMask)); 31 | 32 | truth = Value.isObject(context->this)? context->this.data.boolean->truth: Value.isTrue(context->this); 33 | 34 | return Value.text(truth? &Text(true): &Text(false)); 35 | } 36 | 37 | static 38 | struct Value valueOf (struct Context * const context) 39 | { 40 | int truth; 41 | 42 | Context.assertThisMask(context, Value(booleanMask)); 43 | 44 | truth = Value.isObject(context->this)? context->this.data.boolean->truth: Value.isTrue(context->this); 45 | 46 | return Value.truth(truth); 47 | } 48 | 49 | static 50 | struct Value constructor (struct Context * const context) 51 | { 52 | char truth; 53 | 54 | truth = Value.isTrue(Context.argument(context, 0)); 55 | if (context->construct) 56 | return Value.boolean(Boolean.create(truth)); 57 | else 58 | return Value.truth(truth); 59 | } 60 | 61 | // MARK: - Methods 62 | 63 | void setup () 64 | { 65 | const enum Value(Flags) h = Value(hidden); 66 | 67 | Function.setupBuiltinObject( 68 | &Boolean(constructor), constructor, 1, 69 | &Boolean(prototype), Value.boolean(create(0)), 70 | &Boolean(type)); 71 | 72 | Function.addToObject(Boolean(prototype), "toString", toString, 0, h); 73 | Function.addToObject(Boolean(prototype), "valueOf", valueOf, 0, h); 74 | } 75 | 76 | void teardown (void) 77 | { 78 | Boolean(prototype) = NULL; 79 | Boolean(constructor) = NULL; 80 | } 81 | 82 | struct Boolean * create (int truth) 83 | { 84 | struct Boolean *self = malloc(sizeof(*self)); 85 | *self = Boolean.identity; 86 | Pool.addObject(&self->object); 87 | Object.initialize(&self->object, Boolean(prototype)); 88 | 89 | self->truth = truth; 90 | 91 | return self; 92 | } 93 | -------------------------------------------------------------------------------- /src/builtin/function.h: -------------------------------------------------------------------------------- 1 | // 2 | // function.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_function_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_function_h 17 | 18 | #include "global.h" 19 | #include "../context.h" 20 | #include "../native.h" 21 | #include "../chars.h" 22 | 23 | enum Function(Flags) { 24 | Function(needHeap) = 1 << 1, 25 | Function(needArguments) = 1 << 2, 26 | Function(useBoundThis) = 1 << 3, 27 | Function(strictMode) = 1 << 4, 28 | }; 29 | 30 | extern struct Object * Function(prototype); 31 | extern struct Function * Function(constructor); 32 | extern const struct Object(Type) Function(type); 33 | 34 | #endif 35 | 36 | 37 | Interface(Function, 38 | 39 | (void, setup ,(void)) 40 | (void, teardown ,(void)) 41 | 42 | (struct Function *, create ,(struct Object *environment)) 43 | (struct Function *, createSized ,(struct Object *environment, uint32_t size)) 44 | (struct Function *, createWithNative ,(const Native(Function) native, int parameterCount)) 45 | (struct Function *, copy ,(struct Function * original)) 46 | (void, destroy ,(struct Function *)) 47 | 48 | (void, addMember ,(struct Function *, const char *name, struct Value value, enum Value(Flags))) 49 | (void, addValue ,(struct Function *, const char *name, struct Value value, enum Value(Flags))) 50 | (struct Function *, addMethod ,(struct Function *, const char *name, const Native(Function) native, int argumentCount, enum Value(Flags))) 51 | (struct Function *, addFunction ,(struct Function *, const char *name, const Native(Function) native, int argumentCount, enum Value(Flags))) 52 | (struct Function *, addToObject ,(struct Object *object, const char *name, const Native(Function) native, int parameterCount, enum Value(Flags))) 53 | 54 | (void, linkPrototype ,(struct Function *, struct Value prototype, enum Value(Flags))) 55 | (void, setupBuiltinObject ,(struct Function **, const Native(Function), int parameterCount, struct Object **, struct Value prototype, const struct Object(Type) *type)) 56 | 57 | (struct Value, accessor ,(const Native(Function) getter, const Native(Function) setter)) 58 | , 59 | { 60 | struct Object object; 61 | struct Object environment; 62 | struct Object *refObject; 63 | struct OpList *oplist; 64 | struct Function *pair; 65 | struct Value boundThis; 66 | struct Text text; 67 | const char *name; 68 | int parameterCount; 69 | enum Function(Flags) flags; 70 | } 71 | ) 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/interface.h: -------------------------------------------------------------------------------- 1 | // 2 | // interface.h 3 | // module 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #undef Interface 10 | 11 | #ifdef io_libecc_interface_Implementation 12 | #undef io_libecc_interface_Implementation 13 | 14 | #define Interface(module, methods, object) \ 15 | io_libecc_interface_Declaration(methods) \ 16 | io_libecc_interface_Initialization(module, methods) \ 17 | 18 | #else 19 | 20 | #define Interface(module, methods, object) \ 21 | struct module object; \ 22 | io_libecc_interface_External(module, methods) \ 23 | 24 | #endif 25 | 26 | 27 | #ifndef io_libecc_interface_h 28 | #define io_libecc_interface_h 29 | 30 | #include "compatibility.h" 31 | #include "namespace.h" 32 | 33 | #define io_libecc_interface_External(N, M) struct io_libecc_interface_UnwrapType(N) { io_libecc_interface_CAT(_end, io_libecc_interface_E_even M) const struct N identity; } extern const N; 34 | #define io_libecc_interface_E_even(R, N, P) R io_libecc_interface_Unwrap io_libecc_interface_E(N, P) io_libecc_interface_E_odd 35 | #define io_libecc_interface_E_odd(R, N, P) R io_libecc_interface_Unwrap io_libecc_interface_E(N, P) io_libecc_interface_E_even 36 | #define io_libecc_interface_E_even_end 37 | #define io_libecc_interface_E_odd_end 38 | #define io_libecc_interface_E(N, P) ((*N) P); 39 | 40 | #define io_libecc_interface_Declaration(M) io_libecc_interface_CAT(_end, io_libecc_interface_D_even M) 41 | #define io_libecc_interface_D_even(R, N, P) io_libecc_interface_D(R, N, P) io_libecc_interface_D_odd 42 | #define io_libecc_interface_D_odd(R, N, P) io_libecc_interface_D(R, N, P) io_libecc_interface_D_even 43 | #define io_libecc_interface_D_even_end 44 | #define io_libecc_interface_D_odd_end 45 | #define io_libecc_interface_D(R, N, P) static R N P; 46 | 47 | #define io_libecc_interface_Initialization(N, M) const struct io_libecc_interface_UnwrapType(N) N = { io_libecc_interface_CAT(_end, io_libecc_interface_I_even M) }; 48 | #define io_libecc_interface_I_even(R, N, P) io_libecc_interface_UnwrapComma io_libecc_interface_I(N, P) io_libecc_interface_I_odd 49 | #define io_libecc_interface_I_odd(R, N, P) io_libecc_interface_UnwrapComma io_libecc_interface_I(N, P) io_libecc_interface_I_even 50 | #define io_libecc_interface_I_even_end 51 | #define io_libecc_interface_I_odd_end 52 | #define io_libecc_interface_I(N, P) (N) 53 | 54 | #define io_libecc_interface__CAT(last, entries) entries ## last 55 | #define io_libecc_interface_CAT(last, entries) io_libecc_interface__CAT(last, entries) 56 | 57 | #define io_libecc_interface_Unwrap(x) x 58 | #define io_libecc_interface_UnwrapComma(x) x, 59 | #define io_libecc_interface_UnwrapType(x) type_ ## x 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/builtin/arguments.c: -------------------------------------------------------------------------------- 1 | // 2 | // arguments.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "arguments.h" 11 | 12 | // MARK: - Private 13 | 14 | struct Object * Arguments(prototype); 15 | 16 | const struct Object(Type) Arguments(type) = { 17 | .text = &Text(argumentsType), 18 | }; 19 | 20 | // MARK: - Static Members 21 | 22 | static 23 | struct Value getLength (struct Context * const context) 24 | { 25 | return Value.binary(context->this.data.object->elementCount); 26 | } 27 | 28 | static 29 | struct Value setLength (struct Context * const context) 30 | { 31 | double length; 32 | 33 | length = Value.toBinary(context, Context.argument(context, 0)).data.binary; 34 | if (!isfinite(length) || length < 0 || length > UINT32_MAX || length != (uint32_t)length) 35 | Context.rangeError(context, Chars.create("invalid array length")); 36 | 37 | if (Object.resizeElement(context->this.data.object, length) && context->strictMode) 38 | { 39 | Context.typeError(context, Chars.create("'%u' is non-configurable", context->this.data.object->elementCount)); 40 | } 41 | 42 | return Value(undefined); 43 | } 44 | 45 | static 46 | struct Value getCallee (struct Context * const context) 47 | { 48 | Context.rewindStatement(context->parent); 49 | Context.typeError(context, Chars.create("'callee' cannot be accessed in this context")); 50 | 51 | return Value(undefined); 52 | } 53 | 54 | static 55 | struct Value setCallee (struct Context * const context) 56 | { 57 | Context.rewindStatement(context->parent); 58 | Context.typeError(context, Chars.create("'callee' cannot be accessed in this context")); 59 | 60 | return Value(undefined); 61 | } 62 | 63 | // MARK: - Methods 64 | 65 | void setup (void) 66 | { 67 | const enum Value(Flags) h = Value(hidden); 68 | const enum Value(Flags) s = Value(sealed); 69 | 70 | Arguments(prototype) = Object.createTyped(&Arguments(type)); 71 | 72 | Object.addMember(Arguments(prototype), Key(length), Function.accessor(getLength, setLength), h|s | Value(asOwn) | Value(asData)); 73 | Object.addMember(Arguments(prototype), Key(callee), Function.accessor(getCallee, setCallee), h|s | Value(asOwn)); 74 | } 75 | 76 | void teardown (void) 77 | { 78 | Arguments(prototype) = NULL; 79 | } 80 | 81 | struct Object *createSized (uint32_t size) 82 | { 83 | struct Object *self = Object.create(Arguments(prototype)); 84 | 85 | Object.resizeElement(self, size); 86 | 87 | return self; 88 | } 89 | 90 | struct Object *createWithCList (int count, const char * list[]) 91 | { 92 | struct Object *self = createSized(count); 93 | 94 | Object.populateElementWithCList(self, count, list); 95 | 96 | return self; 97 | } 98 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | // 2 | // context.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_context_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_context_h 17 | 18 | #include "value.h" 19 | 20 | enum Context(Index) { 21 | Context(savedIndexAlt) = -2, 22 | Context(savedIndex) = -1, 23 | Context(noIndex) = 0, 24 | Context(callIndex) = 1, 25 | Context(funcIndex) = 2, 26 | Context(thisIndex) = 3, 27 | }; 28 | 29 | enum Context(Offset) { 30 | Context(accessorOffset) = -1, 31 | Context(callOffset) = 1, 32 | Context(applyOffset) = 2, 33 | }; 34 | 35 | enum Context(Special) { 36 | Context(countMask) = 0x7f, 37 | Context(asAccessor) = 1 << 8, 38 | }; 39 | 40 | #endif 41 | 42 | 43 | Interface(Context, 44 | 45 | (void, rangeError ,(struct Context * const, struct Chars *) Ecc(noreturn)) 46 | (void, referenceError ,(struct Context * const, struct Chars *) Ecc(noreturn)) 47 | (void, syntaxError ,(struct Context * const, struct Chars *) Ecc(noreturn)) 48 | (void, typeError ,(struct Context * const, struct Chars *) Ecc(noreturn)) 49 | (void, uriError ,(struct Context * const, struct Chars *) Ecc(noreturn)) 50 | (void, throw ,(struct Context * const, struct Value) Ecc(noreturn)) 51 | 52 | (struct Value, callFunction ,(struct Context * const, struct Function *function, struct Value this, int argumentCount, ... )) 53 | 54 | (int, argumentCount ,(struct Context * const)) 55 | (struct Value, argument ,(struct Context * const, int argumentIndex)) 56 | (void, replaceArgument ,(struct Context * const, int argumentIndex, struct Value value)) 57 | 58 | (struct Value, this ,(struct Context * const)) 59 | (void, assertThisType ,(struct Context * const, enum Value(Type))) 60 | (void, assertThisMask ,(struct Context * const, enum Value(Mask))) 61 | (void, assertThisCoerciblePrimitive ,(struct Context * const)) 62 | 63 | (void, setText ,(struct Context * const, const struct Text *text)) 64 | (void, setTexts ,(struct Context * const, const struct Text *text, const struct Text *textAlt)) 65 | (void, setTextIndex ,(struct Context * const, enum Context(Index) index)) 66 | (void, setTextIndexArgument ,(struct Context * const, int argument)) 67 | (struct Text, textSeek ,(struct Context * const)) 68 | 69 | (void, rewindStatement ,(struct Context * const)) 70 | (void, printBacktrace ,(struct Context * const context)) 71 | 72 | (struct Object *, environmentRoot ,(struct Context * const context)) 73 | , 74 | { 75 | const struct Op * ops; 76 | struct Object * refObject; 77 | struct Object * environment; 78 | struct Context * parent; 79 | struct Ecc * ecc; 80 | struct Value this; 81 | 82 | const struct Text * text; 83 | const struct Text * textAlt; 84 | const struct Text * textCall; 85 | enum Context(Index) textIndex; 86 | 87 | int16_t breaker; 88 | int16_t depth; 89 | int8_t construct:1; 90 | int8_t argumentOffset:3; 91 | int8_t strictMode:1; 92 | int8_t inEnvironmentObject:1; 93 | } 94 | ) 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/lexer.h: -------------------------------------------------------------------------------- 1 | // 2 | // lexer.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_lexer_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_lexer_h 17 | 18 | #include "builtin/error.h" 19 | #include "input.h" 20 | 21 | #define io_libecc_lexer_Tokens \ 22 | _( no ,"end of script",= 0)\ 23 | _( error ,,= 128)\ 24 | \ 25 | /* literal */\ 26 | _( null ,,)\ 27 | _( true ,,)\ 28 | _( false ,,)\ 29 | _( integer ,"number",)\ 30 | _( binary ,"number",)\ 31 | _( string ,,)\ 32 | _( escapedString ,"string",)\ 33 | _( identifier ,,)\ 34 | _( regexp ,,)\ 35 | \ 36 | /* keyword */\ 37 | _( break ,,)\ 38 | _( case ,,)\ 39 | _( catch ,,)\ 40 | _( continue ,,)\ 41 | _( debugger ,,)\ 42 | _( default ,,)\ 43 | _( delete ,,)\ 44 | _( do ,,)\ 45 | _( else ,,)\ 46 | _( finally ,,)\ 47 | _( for ,,)\ 48 | _( function ,,)\ 49 | _( if ,,)\ 50 | _( in ,,)\ 51 | _( instanceof ,,)\ 52 | _( new ,,)\ 53 | _( return ,,)\ 54 | _( switch ,,)\ 55 | _( this ,,)\ 56 | _( throw ,,)\ 57 | _( try ,,)\ 58 | _( typeof ,,)\ 59 | _( var ,,)\ 60 | _( void ,,)\ 61 | _( with ,,)\ 62 | _( while ,,)\ 63 | \ 64 | /* operator */\ 65 | _( equal ,"'=='",)\ 66 | _( notEqual ,"'!='",)\ 67 | _( identical ,"'==='",)\ 68 | _( notIdentical ,"'!=='",)\ 69 | _( leftShiftAssign ,"'<<='",)\ 70 | _( rightShiftAssign ,"'>>='",)\ 71 | _( unsignedRightShiftAssign ,"'>>>='",)\ 72 | _( leftShift ,"'<<'",)\ 73 | _( rightShift ,"'>>'",)\ 74 | _( unsignedRightShift ,"'>>>'",)\ 75 | _( lessOrEqual ,"'<='",)\ 76 | _( moreOrEqual ,"'>='",)\ 77 | _( increment ,"'++'",)\ 78 | _( decrement ,"'--'",)\ 79 | _( logicalAnd ,"'&&'",)\ 80 | _( logicalOr ,"'||'",)\ 81 | _( addAssign ,"'+='",)\ 82 | _( minusAssign ,"'-='",)\ 83 | _( multiplyAssign ,"'*='",)\ 84 | _( divideAssign ,"'/='",)\ 85 | _( moduloAssign ,"'%='",)\ 86 | _( andAssign ,"'&='",)\ 87 | _( orAssign ,"'|='",)\ 88 | _( xorAssign ,"'^='",)\ 89 | \ 90 | 91 | #define _(X, S, V) Lexer(X ## Token) V, 92 | enum Lexer(Token) { 93 | io_libecc_lexer_Tokens 94 | }; 95 | #undef _ 96 | 97 | enum Lexer(ScanFlags) { 98 | Lexer(scanLazy) = 1 << 0, 99 | Lexer(scanSloppy) = 1 << 1, 100 | }; 101 | 102 | #endif 103 | 104 | 105 | Interface(Lexer, 106 | 107 | (struct Lexer *, createWithInput ,(struct Input *)) 108 | (void, destroy, (struct Lexer *)) 109 | 110 | (enum Lexer(Token), nextToken, (struct Lexer *)) 111 | 112 | (const char *, tokenChars ,(enum Lexer(Token) token, char buffer[4])) 113 | 114 | (struct Value, scanBinary ,(struct Text text, enum Lexer(ScanFlags))) 115 | (struct Value, scanInteger ,(struct Text text, int base, enum Lexer(ScanFlags))) 116 | (uint32_t, scanElement ,(struct Text text)) 117 | 118 | (uint8_t, uint8Hex ,(char a, char b)) 119 | (uint16_t, uint16Hex ,(char a, char b, char c, char d)) 120 | , 121 | { 122 | struct Input *input; 123 | uint32_t offset; 124 | 125 | struct Value value; 126 | struct Text text; 127 | int didLineBreak; 128 | int allowRegex; 129 | int allowUnicodeOutsideLiteral; 130 | int disallowKeyword; 131 | } 132 | ) 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /src/text.h: -------------------------------------------------------------------------------- 1 | // 2 | // text.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_text_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_text_h 17 | 18 | extern const struct Text Text(undefined); 19 | extern const struct Text Text(null); 20 | extern const struct Text Text(false); 21 | extern const struct Text Text(true); 22 | extern const struct Text Text(boolean); 23 | extern const struct Text Text(number); 24 | extern const struct Text Text(string); 25 | extern const struct Text Text(object); 26 | extern const struct Text Text(function); 27 | extern const struct Text Text(zero); 28 | extern const struct Text Text(one); 29 | extern const struct Text Text(nan); 30 | extern const struct Text Text(infinity); 31 | extern const struct Text Text(negativeInfinity); 32 | extern const struct Text Text(nativeCode); 33 | extern const struct Text Text(empty); 34 | extern const struct Text Text(emptyRegExp); 35 | 36 | extern const struct Text Text(nullType); 37 | extern const struct Text Text(undefinedType); 38 | extern const struct Text Text(objectType); 39 | extern const struct Text Text(errorType); 40 | extern const struct Text Text(arrayType); 41 | extern const struct Text Text(stringType); 42 | extern const struct Text Text(regexpType); 43 | extern const struct Text Text(numberType); 44 | extern const struct Text Text(booleanType); 45 | extern const struct Text Text(dateType); 46 | extern const struct Text Text(functionType); 47 | extern const struct Text Text(argumentsType); 48 | extern const struct Text Text(mathType); 49 | extern const struct Text Text(jsonType); 50 | extern const struct Text Text(globalType); 51 | 52 | extern const struct Text Text(errorName); 53 | extern const struct Text Text(rangeErrorName); 54 | extern const struct Text Text(referenceErrorName); 55 | extern const struct Text Text(syntaxErrorName); 56 | extern const struct Text Text(typeErrorName); 57 | extern const struct Text Text(uriErrorName); 58 | extern const struct Text Text(inputErrorName); 59 | extern const struct Text Text(evalErrorName); 60 | 61 | struct Text(Char) { 62 | uint32_t codepoint; 63 | uint8_t units; 64 | }; 65 | 66 | enum Text(Flags) { 67 | Text(breakFlag) = 1 << 0, 68 | }; 69 | 70 | #endif 71 | 72 | 73 | Interface(Text, 74 | 75 | (struct Text, make ,(const char *bytes, int32_t length)) 76 | (struct Text, join ,(struct Text from, struct Text to)) 77 | 78 | (struct Text(Char), character ,(struct Text)) 79 | (struct Text(Char), nextCharacter ,(struct Text *text)) 80 | (struct Text(Char), prevCharacter ,(struct Text *text)) 81 | (void, advance ,(struct Text *text, int32_t units)) 82 | 83 | (uint16_t, toUTF16Length ,(struct Text)) 84 | (uint16_t, toUTF16 ,(struct Text, uint16_t *wbuffer)) 85 | 86 | (char *, toLower ,(struct Text, char *x2buffer)) 87 | (char *, toUpper ,(struct Text, char *x3buffer)) 88 | 89 | (int, isSpace ,(struct Text(Char))) 90 | (int, isDigit ,(struct Text(Char))) 91 | (int, isWord ,(struct Text(Char))) 92 | (int, isLineFeed ,(struct Text(Char))) 93 | , 94 | { 95 | const char *bytes; 96 | int32_t length; 97 | uint8_t flags; 98 | } 99 | ) 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/key.c: -------------------------------------------------------------------------------- 1 | // 2 | // key.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "key.h" 11 | 12 | #include "env.h" 13 | #include "lexer.h" 14 | #include "ecc.h" 15 | #include "builtin/object.h" 16 | 17 | // MARK: - Private 18 | 19 | static struct Text *keyPool = NULL; 20 | static uint16_t keyCount = 0; 21 | static uint16_t keyCapacity = 0; 22 | 23 | static char **charsList = NULL; 24 | static uint16_t charsCount = 0; 25 | 26 | struct Key Key(none) = {{{ 0 }}}; 27 | 28 | #define _(X) struct Key Key(X); 29 | io_libecc_key_Keys 30 | #undef _ 31 | 32 | // MARK: - Static Members 33 | 34 | static 35 | struct Key makeWithNumber (uint16_t number) 36 | { 37 | struct Key key; 38 | 39 | key.data.depth[0] = number >> 12 & 0xf; 40 | key.data.depth[1] = number >> 8 & 0xf; 41 | key.data.depth[2] = number >> 4 & 0xf; 42 | key.data.depth[3] = number >> 0 & 0xf; 43 | 44 | return key; 45 | } 46 | 47 | static 48 | struct Key addWithText (const struct Text text, enum Key(Flags) flags) 49 | { 50 | if (keyCount >= keyCapacity) 51 | { 52 | if (keyCapacity == UINT16_MAX) 53 | Ecc.fatal("No more identifier left"); 54 | 55 | keyCapacity += 0xff; 56 | keyPool = realloc(keyPool, keyCapacity * sizeof(*keyPool)); 57 | } 58 | 59 | if ((isdigit(text.bytes[0]) || text.bytes[0] == '-') && !isnan(Lexer.scanBinary(text, 0).data.binary)) 60 | Env.printWarning("Creating identifier '%.*s'; %u identifier(s) left. Using array of length > 0x%x, or negative-integer/floating-point as property name is discouraged", text.length, text.bytes, UINT16_MAX - keyCount, Object(ElementMax)); 61 | 62 | if (flags & Key(copyOnCreate)) 63 | { 64 | char *chars = malloc(text.length + 1); 65 | memcpy(chars, text.bytes, text.length); 66 | chars[text.length] = '\0'; 67 | charsList = realloc(charsList, sizeof(*charsList) * (charsCount + 1)); 68 | charsList[charsCount++] = chars; 69 | keyPool[keyCount++] = Text.make(chars, text.length); 70 | } 71 | else 72 | keyPool[keyCount++] = text; 73 | 74 | return makeWithNumber(keyCount); 75 | } 76 | 77 | // MARK: - Methods 78 | 79 | void setup (void) 80 | { 81 | if (!keyPool) 82 | { 83 | #define _(X) Key(X) = addWithText(Text.make(#X, strlen(#X)), 0); 84 | io_libecc_key_Keys 85 | #undef _ 86 | } 87 | } 88 | 89 | void teardown (void) 90 | { 91 | while (charsCount) 92 | free(charsList[--charsCount]), charsList[charsCount] = NULL; 93 | 94 | free(charsList), charsList = NULL, charsCount = 0; 95 | free(keyPool), keyPool = NULL, keyCount = 0, keyCapacity = 0; 96 | } 97 | 98 | struct Key makeWithCString (const char *cString) 99 | { 100 | return makeWithText(Text.make(cString, (uint16_t)strlen(cString)), 0); 101 | } 102 | 103 | struct Key makeWithText (const struct Text text, enum Key(Flags) flags) 104 | { 105 | struct Key key = search(text); 106 | 107 | if (!key.data.integer) 108 | key = addWithText(text, flags); 109 | 110 | return key; 111 | } 112 | 113 | struct Key search (const struct Text text) 114 | { 115 | uint16_t index = 0; 116 | 117 | for (index = 0; index < keyCount; ++index) 118 | { 119 | if (text.length == keyPool[index].length && memcmp(keyPool[index].bytes, text.bytes, text.length) == 0) 120 | { 121 | return makeWithNumber(index + 1); 122 | } 123 | } 124 | return makeWithNumber(0); 125 | } 126 | 127 | int isEqual (struct Key self, struct Key to) 128 | { 129 | return self.data.integer == to.data.integer; 130 | } 131 | 132 | const struct Text *textOf (struct Key key) 133 | { 134 | uint16_t number = key.data.depth[0] << 12 | key.data.depth[1] << 8 | key.data.depth[2] << 4 | key.data.depth[3]; 135 | if (number) 136 | return &keyPool[number - 1]; 137 | else 138 | return &Text(empty); 139 | } 140 | 141 | void dumpTo (struct Key key, FILE *file) 142 | { 143 | const struct Text *text = textOf(key); 144 | fprintf(file, "%.*s", (int)text->length, text->bytes); 145 | } 146 | -------------------------------------------------------------------------------- /src/op.h: -------------------------------------------------------------------------------- 1 | // 2 | // op.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_op_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_op_h 17 | 18 | #include "builtin/function.h" 19 | 20 | #define io_libecc_op_List \ 21 | \ 22 | /* expression */\ 23 | _( noop )\ 24 | _( value )\ 25 | _( valueConstRef )\ 26 | _( text )\ 27 | _( function )\ 28 | _( object )\ 29 | _( array )\ 30 | _( regexp )\ 31 | _( this )\ 32 | \ 33 | _( createLocalRef )\ 34 | _( getLocalRefOrNull )\ 35 | _( getLocalRef )\ 36 | _( getLocal )\ 37 | _( setLocal )\ 38 | _( deleteLocal )\ 39 | \ 40 | _( getLocalSlotRef )\ 41 | _( getLocalSlot )\ 42 | _( setLocalSlot )\ 43 | _( deleteLocalSlot )\ 44 | \ 45 | _( getParentSlotRef )\ 46 | _( getParentSlot )\ 47 | _( setParentSlot )\ 48 | _( deleteParentSlot )\ 49 | \ 50 | _( getMemberRef )\ 51 | _( getMember )\ 52 | _( setMember )\ 53 | _( callMember )\ 54 | _( deleteMember )\ 55 | \ 56 | _( getPropertyRef )\ 57 | _( getProperty )\ 58 | _( setProperty )\ 59 | _( callProperty )\ 60 | _( deleteProperty )\ 61 | \ 62 | _( pushEnvironment )\ 63 | _( popEnvironment )\ 64 | _( exchange )\ 65 | _( typeOf )\ 66 | _( equal )\ 67 | _( notEqual )\ 68 | _( identical )\ 69 | _( notIdentical )\ 70 | _( less )\ 71 | _( lessOrEqual )\ 72 | _( more )\ 73 | _( moreOrEqual )\ 74 | _( instanceOf )\ 75 | _( in )\ 76 | _( add )\ 77 | _( minus )\ 78 | _( multiply )\ 79 | _( divide )\ 80 | _( modulo )\ 81 | _( leftShift )\ 82 | _( rightShift )\ 83 | _( unsignedRightShift )\ 84 | _( bitwiseAnd )\ 85 | _( bitwiseXor )\ 86 | _( bitwiseOr )\ 87 | _( logicalAnd )\ 88 | _( logicalOr )\ 89 | _( positive )\ 90 | _( negative )\ 91 | _( invert )\ 92 | _( not )\ 93 | _( construct )\ 94 | _( call )\ 95 | _( eval )\ 96 | \ 97 | /* assignement expression */\ 98 | _( incrementRef )\ 99 | _( decrementRef )\ 100 | _( postIncrementRef )\ 101 | _( postDecrementRef )\ 102 | _( addAssignRef )\ 103 | _( minusAssignRef )\ 104 | _( multiplyAssignRef )\ 105 | _( divideAssignRef )\ 106 | _( moduloAssignRef )\ 107 | _( leftShiftAssignRef )\ 108 | _( rightShiftAssignRef )\ 109 | _( unsignedRightShiftAssignRef )\ 110 | _( bitAndAssignRef )\ 111 | _( bitXorAssignRef )\ 112 | _( bitOrAssignRef )\ 113 | \ 114 | /* statement */\ 115 | _( debugger )\ 116 | _( try )\ 117 | _( throw )\ 118 | _( with )\ 119 | _( next )\ 120 | _( nextIf )\ 121 | _( autoreleaseExpression )\ 122 | _( autoreleaseDiscard )\ 123 | _( expression )\ 124 | _( discard )\ 125 | _( discardN )\ 126 | _( jump )\ 127 | _( jumpIf )\ 128 | _( jumpIfNot )\ 129 | _( repopulate )\ 130 | _( result )\ 131 | _( resultVoid )\ 132 | _( switchOp )\ 133 | _( breaker )\ 134 | _( iterate )\ 135 | _( iterateLessRef )\ 136 | _( iterateMoreRef )\ 137 | _( iterateLessOrEqualRef )\ 138 | _( iterateMoreOrEqualRef )\ 139 | _( iterateInRef )\ 140 | 141 | #endif 142 | 143 | 144 | #define _(X) (struct Value, X , (struct Context * const)) 145 | Interface(Op, 146 | 147 | (struct Op, make ,(const Native(Function) native, struct Value value, struct Text text)) 148 | (const char *, toChars ,(const Native(Function) native)) 149 | 150 | (struct Value, callFunctionArguments ,(struct Context * const, enum Context(Offset), struct Function *function, struct Value this, struct Object *arguments)) 151 | (struct Value, callFunctionVA ,(struct Context * const, enum Context(Offset), struct Function *function, struct Value this, int argumentCount, va_list ap)) 152 | 153 | io_libecc_op_List 154 | , 155 | { 156 | Native(Function) native; 157 | struct Value value; 158 | struct Text text; 159 | } 160 | ) 161 | #undef _ 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /src/namespace.h: -------------------------------------------------------------------------------- 1 | // 2 | // namespace_io_libecc.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_namespace_h 10 | #define io_libecc_namespace_h 11 | 12 | #define Ecc io_libecc_Ecc 13 | #define io_libecc_Ecc(X) \ 14 | io_libecc_ecc_## X 15 | 16 | #define Input io_libecc_Input 17 | #define io_libecc_Input(X) \ 18 | io_libecc_input_## X 19 | 20 | #define Lexer io_libecc_Lexer 21 | #define io_libecc_Lexer(X) \ 22 | io_libecc_lexer_## X 23 | 24 | #define Parser io_libecc_Parser 25 | #define io_libecc_Parser(X) \ 26 | io_libecc_parser_## X 27 | 28 | #define Value io_libecc_Value 29 | #define io_libecc_Value(X) \ 30 | io_libecc_value_## X 31 | 32 | #define Op io_libecc_Op 33 | #define io_libecc_Op(X) \ 34 | io_libecc_op_## X 35 | 36 | #define OpList io_libecc_OpList 37 | #define io_libecc_OpList(X) \ 38 | io_libecc_oplist_## X 39 | 40 | #define Date io_libecc_Date 41 | #define io_libecc_Date(X) \ 42 | io_libecc_date_## X 43 | 44 | #define Key io_libecc_Key 45 | #define io_libecc_Key(X) \ 46 | io_libecc_key_## X 47 | 48 | #define Native io_libecc_Native 49 | #define io_libecc_Native(X) \ 50 | io_libecc_native_## X 51 | 52 | #define Context io_libecc_Context 53 | #define io_libecc_Context(X) \ 54 | io_libecc_context_## X 55 | 56 | #define Pool io_libecc_Pool 57 | #define io_libecc_Pool(X) \ 58 | io_libecc_pool_## X 59 | 60 | #define Env io_libecc_Env 61 | #define io_libecc_Env(X) \ 62 | io_libecc_env_## X 63 | 64 | #define Text io_libecc_Text 65 | #define io_libecc_Text(X) \ 66 | io_libecc_text_## X 67 | 68 | #define Chars io_libecc_Chars 69 | #define io_libecc_Chars(X) \ 70 | io_libecc_chars_## X 71 | 72 | #define Entry io_libecc_Entry 73 | #define io_libecc_Entry(X) \ 74 | io_libecc_entry_## X 75 | 76 | #define Object io_libecc_Object 77 | #define io_libecc_Object(X) \ 78 | io_libecc_object_## X 79 | 80 | #define Array io_libecc_Array 81 | #define io_libecc_Array(X) \ 82 | io_libecc_array_## X 83 | 84 | #define Arguments io_libecc_Arguments 85 | #define io_libecc_Arguments(X) \ 86 | io_libecc_arguments_## X 87 | 88 | #define Error io_libecc_Error 89 | #define io_libecc_Error(X) \ 90 | io_libecc_error_## X 91 | 92 | #define String io_libecc_String 93 | #define io_libecc_String(X) \ 94 | io_libecc_string_## X 95 | 96 | #define Function io_libecc_Function 97 | #define io_libecc_Function(X) \ 98 | io_libecc_function_## X 99 | 100 | #define Math io_libecc_Math 101 | #define io_libecc_Math(X) \ 102 | io_libecc_math_## X 103 | 104 | #define Number io_libecc_Number 105 | #define io_libecc_Number(X) \ 106 | io_libecc_number_## X 107 | 108 | #define Boolean io_libecc_Boolean 109 | #define io_libecc_Boolean(X) \ 110 | io_libecc_boolean_## X 111 | 112 | #define RegExp io_libecc_RegExp 113 | #define io_libecc_RegExp(X) \ 114 | io_libecc_regexp_## X 115 | 116 | #define JSON io_libecc_JSON 117 | #define io_libecc_JSON(X) \ 118 | io_libecc_json_## X 119 | 120 | #define Global io_libecc_Global 121 | #define io_libecc_Global(X) \ 122 | io_libecc_global_## X 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/builtin/object.h: -------------------------------------------------------------------------------- 1 | // 2 | // object.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_object_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "../implementation.h" 14 | #else 15 | #include "../interface.h" 16 | #define io_libecc_object_h 17 | 18 | #include "../value.h" 19 | 20 | struct Object(Type) 21 | { 22 | const struct Text *text; 23 | 24 | void (*mark)(struct Object *); 25 | void (*capture)(struct Object *); 26 | void (*finalize)(struct Object *); 27 | }; 28 | 29 | enum Object(Flags) 30 | { 31 | Object(mark) = 1 << 0, 32 | Object(sealed) = 1 << 1, 33 | }; 34 | 35 | extern struct Object * Object(prototype); 36 | extern struct Function * Object(constructor); 37 | extern const struct Object(Type) Object(type); 38 | 39 | extern const uint32_t Object(ElementMax); 40 | 41 | #endif 42 | 43 | 44 | Interface(Object, 45 | 46 | (void, setup ,(void)) 47 | (void, teardown ,(void)) 48 | 49 | (struct Object *, create ,(struct Object * prototype)) 50 | (struct Object *, createSized ,(struct Object * prototype, uint16_t size)) 51 | (struct Object *, createTyped ,(const struct Object(Type) *type)) 52 | (struct Object *, initialize ,(struct Object * restrict, struct Object * restrict prototype)) 53 | (struct Object *, initializeSized ,(struct Object * restrict, struct Object * restrict prototype, uint16_t size)) 54 | (struct Object *, finalize ,(struct Object *)) 55 | (struct Object *, copy ,(const struct Object * original)) 56 | (void, destroy ,(struct Object *)) 57 | 58 | (struct Value, getMember ,(struct Context * const, struct Object *, struct Key key)) 59 | (struct Value, putMember ,(struct Context * const, struct Object *, struct Key key, struct Value)) 60 | (struct Value *, member ,(struct Object *, struct Key key, enum Value(Flags))) 61 | (struct Value *, addMember ,(struct Object *, struct Key key, struct Value, enum Value(Flags))) 62 | (int, deleteMember ,(struct Object *, struct Key key)) 63 | 64 | (struct Value, getElement ,(struct Context * const, struct Object *, uint32_t index)) 65 | (struct Value, putElement ,(struct Context * const, struct Object *, uint32_t index, struct Value)) 66 | (struct Value *, element ,(struct Object *, uint32_t index, enum Value(Flags))) 67 | (struct Value *, addElement ,(struct Object *, uint32_t index, struct Value, enum Value(Flags))) 68 | (int, deleteElement ,(struct Object *, uint32_t index)) 69 | 70 | (struct Value, getProperty ,(struct Context * const, struct Object *, struct Value primitive)) 71 | (struct Value, putProperty ,(struct Context * const, struct Object *, struct Value primitive, struct Value)) 72 | (struct Value *, property ,(struct Object *, struct Value primitive, enum Value(Flags))) 73 | (struct Value *, addProperty ,(struct Object *, struct Value primitive, struct Value, enum Value(Flags))) 74 | (int, deleteProperty ,(struct Object *, struct Value primitive)) 75 | 76 | (struct Value, putValue ,(struct Context * const, struct Object *, struct Value *, struct Value)) 77 | (struct Value, getValue ,(struct Context * const, struct Object *, struct Value *)) 78 | 79 | (void, packValue ,(struct Object *)) 80 | (void, stripMap ,(struct Object *)) 81 | (void, reserveSlots ,(struct Object *, uint16_t slots)) 82 | 83 | (int, resizeElement ,(struct Object *, uint32_t size)) 84 | (void, populateElementWithCList ,(struct Object *, uint32_t count, const char * list[])) 85 | 86 | (struct Value, toString ,(struct Context * const)) 87 | (void, dumpTo ,(struct Object *, FILE *file)) 88 | , 89 | { 90 | struct Object *prototype; 91 | 92 | const struct Object(Type) *type; 93 | 94 | union Object(Element) { 95 | struct Value value; 96 | } *element; 97 | 98 | union Object(Hashmap) { 99 | struct Value value; 100 | uint16_t slot[16]; 101 | } *hashmap; 102 | 103 | uint32_t elementCount; 104 | uint32_t elementCapacity; 105 | uint16_t hashmapCount; 106 | uint16_t hashmapCapacity; 107 | 108 | int16_t referenceCount; 109 | uint8_t flags; 110 | } 111 | ) 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/env.c: -------------------------------------------------------------------------------- 1 | // 2 | // env.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "env.h" 11 | 12 | #if __MSDOS__ 13 | #include 14 | #elif _WIN32 15 | #define WIN32_LEAN_AND_MEAN 16 | #include 17 | #endif 18 | 19 | #if _WIN32 20 | #include 21 | #elif _DEFAULT_SOURCE || __APPLE__ 22 | #include 23 | #endif 24 | 25 | // MARK: - Private 26 | 27 | #define PRINT_MAX 2048 28 | 29 | const int Env(print_max) = PRINT_MAX; 30 | 31 | struct { 32 | #if __MSDOS__ 33 | int attribute; 34 | #elif _WIN32 35 | HANDLE console; 36 | int attribute; 37 | int cp; 38 | #else 39 | int terminal; 40 | #endif 41 | } static env; 42 | 43 | void setup(void) 44 | { 45 | srand((unsigned)time(NULL)); 46 | 47 | #if __MSDOS__ 48 | struct text_info textInfo; 49 | gettextinfo(&textInfo); 50 | env.attribute = textInfo.normattr; 51 | #elif _WIN32 52 | CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo; 53 | env.console = GetStdHandle(STD_ERROR_HANDLE); 54 | env.cp = GetConsoleOutputCP(); 55 | SetConsoleOutputCP(CP_UTF8); 56 | GetConsoleScreenBufferInfo(env.console, &consoleScreenBufferInfo); 57 | env.attribute = consoleScreenBufferInfo.wAttributes; 58 | #else 59 | env.terminal = getenv("TERM") != NULL; 60 | #endif 61 | } 62 | 63 | void teardown(void) 64 | { 65 | #if __MSDOS__ 66 | textattr(env.attribute); 67 | #elif _WIN32 68 | SetConsoleOutputCP(env.cp); 69 | SetConsoleTextAttribute(env.console, env.attribute); 70 | #endif 71 | 72 | newline(); 73 | } 74 | 75 | static 76 | void textc(enum Env(Color) c, enum Env(Attribute) a) 77 | { 78 | #if __MSDOS__ || _WIN32 79 | if (a == Env(invisible)) 80 | c = (env.attribute >> 4) & 0x7; 81 | else if (c == Env(white) || !c) 82 | c = a == Env(bold)? 0xf: 0x7; 83 | else if (c == Env(black)) 84 | c = a == Env(bold)? 0x7: 0x8; 85 | else 86 | { 87 | c -= 30; 88 | c = (a == Env(bold)? 0x8: 0) 89 | | ((c << 2) & 0x4) 90 | | (c & 0x2) 91 | | ((c >> 2) & 0x1) 92 | ; 93 | } 94 | c |= env.attribute & 0xf0; 95 | 96 | #if __MSDOS__ 97 | textattr(c); 98 | #elif _WIN32 99 | SetConsoleTextAttribute(env.console, c); 100 | #endif 101 | #else 102 | if (env.terminal) 103 | { 104 | if (c && a) 105 | fprintf(stderr, "\x1B[%d;%dm", c, a); 106 | else if (c | a) 107 | fprintf(stderr, "\x1B[%dm", c | a); 108 | else 109 | fprintf(stderr, "\x1B[0m"); 110 | } 111 | #endif 112 | } 113 | 114 | static 115 | void vprintc(const char *format, va_list ap) 116 | { 117 | char buffer[PRINT_MAX]; 118 | size_t size = sizeof(buffer); 119 | 120 | if (vsnprintf(buffer, size, format, ap) >= size) 121 | { 122 | buffer[size - 3] = '.'; 123 | buffer[size - 2] = '.'; 124 | } 125 | #if __MSDOS__ 126 | cputs(buffer); 127 | #elif _WIN32 128 | { 129 | DWORD count; 130 | WriteConsoleA(env.console, buffer, strlen(buffer), &count, NULL); 131 | } 132 | #else 133 | fputs(buffer, stderr); 134 | #endif 135 | } 136 | 137 | void print(const char *format, ...) 138 | { 139 | va_list ap; 140 | 141 | va_start(ap, format); 142 | vprintc(format, ap); 143 | va_end(ap); 144 | } 145 | 146 | void printColor(enum Env(Color) color, enum Env(Attribute) attribute, const char *format, ...) 147 | { 148 | va_list ap; 149 | 150 | va_start(ap, format); 151 | textc(color, attribute); 152 | vprintc(format, ap); 153 | textc(0, 0); 154 | va_end(ap); 155 | } 156 | 157 | void printError (int typeLength, const char *type, const char *format, ...) 158 | { 159 | va_list ap; 160 | 161 | printColor(Env(red), Env(bold), "%.*s", typeLength, type); 162 | print(": "); 163 | 164 | va_start(ap, format); 165 | textc(0, Env(bold)); 166 | vprintc(format, ap); 167 | textc(0, 0); 168 | va_end(ap); 169 | 170 | newline(); 171 | } 172 | 173 | void printWarning (const char *format, ...) 174 | { 175 | va_list ap; 176 | 177 | printColor(Env(yellow), Env(bold), "Warning"); 178 | print(": "); 179 | 180 | va_start(ap, format); 181 | textc(0, Env(bold)); 182 | vprintc(format, ap); 183 | textc(0, 0); 184 | va_end(ap); 185 | 186 | newline(); 187 | } 188 | 189 | void newline() 190 | { 191 | #if __MSDOS__ || _WIN32 192 | putc('\r', stderr); 193 | #endif 194 | putc('\n', stderr); 195 | } 196 | 197 | double currentTime () 198 | { 199 | #if _WIN32 200 | struct _timeb timebuffer; 201 | _ftime (&timebuffer); 202 | return timebuffer.time * 1000 + timebuffer.millitm; 203 | #elif _DEFAULT_SOURCE || __APPLE__ 204 | struct timeval time; 205 | gettimeofday(&time, NULL); 206 | return time.tv_sec * 1000 + time.tv_usec / 1000; 207 | #else 208 | return time(NULL) * 1000; 209 | #endif 210 | } 211 | -------------------------------------------------------------------------------- /src/input.c: -------------------------------------------------------------------------------- 1 | // 2 | // input.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "input.h" 11 | 12 | #include "chars.h" 13 | 14 | // MARK: - Private 15 | 16 | // MARK: - Static Members 17 | 18 | static 19 | struct Input * create() 20 | { 21 | size_t linesBytes; 22 | struct Input *self = malloc(sizeof(*self)); 23 | *self = Input.identity; 24 | 25 | self->lineCapacity = 8; 26 | self->lineCount = 1; 27 | 28 | linesBytes = sizeof(*self->lines) * self->lineCapacity; 29 | self->lines = malloc(linesBytes); 30 | memset(self->lines, 0, linesBytes); 31 | 32 | return self; 33 | } 34 | 35 | static 36 | void printInput (const char *name, uint16_t line) 37 | { 38 | if (name[0] == '(') 39 | Env.printColor(0, Env(dim), "%s", name); 40 | else 41 | Env.printColor(0, Env(bold), "%s", name); 42 | 43 | Env.printColor(0, Env(bold), " line:%d", line); 44 | } 45 | 46 | // MARK: - Methods 47 | 48 | struct Input * createFromFile (const char *filename) 49 | { 50 | struct Text inputError = Text(inputErrorName); 51 | FILE *file; 52 | long size; 53 | struct Input *self; 54 | 55 | assert(filename); 56 | 57 | file = fopen(filename, "rb"); 58 | if (!file) 59 | { 60 | Env.printError(inputError.length, inputError.bytes, "cannot open file '%s'", filename); 61 | return NULL; 62 | } 63 | 64 | if (fseek(file, 0, SEEK_END) || (size = ftell(file)) < 0 || fseek(file, 0, SEEK_SET)) 65 | { 66 | Env.printError(inputError.length, inputError.bytes, "cannot handle file '%s'", filename); 67 | fclose(file); 68 | return NULL; 69 | } 70 | 71 | self = create(); 72 | 73 | strncat(self->name, filename, sizeof(self->name) - 1); 74 | self->bytes = malloc(size + 1); 75 | self->length = (uint32_t)fread(self->bytes, sizeof(char), size, file); 76 | fclose(file), file = NULL; 77 | self->bytes[size] = '\0'; 78 | 79 | // FILE *f = fopen("error.txt", "w"); 80 | // fprintf(f, "%.*s", self->length, self->bytes); 81 | // fclose(f); 82 | 83 | return self; 84 | } 85 | 86 | struct Input * createFromBytes (const char *bytes, uint32_t length, const char *name, ...) 87 | { 88 | struct Input *self; 89 | 90 | assert(bytes); 91 | 92 | self = create(); 93 | 94 | if (name) 95 | { 96 | va_list ap; 97 | va_start(ap, name); 98 | vsnprintf(self->name, sizeof(self->name), name, ap); 99 | va_end(ap); 100 | } 101 | self->length = length; 102 | self->bytes = malloc(length + 1); 103 | memcpy(self->bytes, bytes, length); 104 | self->bytes[length] = '\0'; 105 | 106 | return self; 107 | } 108 | 109 | void destroy (struct Input *self) 110 | { 111 | assert(self); 112 | 113 | free(self->attached), self->attached = NULL; 114 | free(self->bytes), self->bytes = NULL; 115 | free(self->lines), self->lines = NULL; 116 | free(self), self = NULL; 117 | } 118 | 119 | void printText (struct Input *self, struct Text text, int32_t ofLine, struct Text ofText, const char *ofInput, int fullLine) 120 | { 121 | int32_t line = -1; 122 | const char *bytes = NULL; 123 | uint16_t length = 0; 124 | 125 | if (ofText.length) 126 | { 127 | bytes = ofText.bytes; 128 | length = ofText.length; 129 | printInput(ofInput? ofInput: "native code", ofLine? ofLine: 1); 130 | } 131 | else if (!self) 132 | printInput(ofInput? ofInput: "(unknown input)", 0); 133 | else 134 | { 135 | line = findLine(self, text); 136 | printInput(ofInput? ofInput: self->name, line > 0? line: 0); 137 | 138 | if (line > 0) 139 | { 140 | uint32_t start = self->lines[line]; 141 | 142 | bytes = self->bytes + start; 143 | do 144 | { 145 | if (!isblank(bytes[length]) && !isgraph(bytes[length]) && bytes[length] >= 0) 146 | break; 147 | 148 | ++length; 149 | } while (start + length < self->length); 150 | } 151 | } 152 | 153 | if (!fullLine) 154 | { 155 | if (text.length) 156 | Env.printColor(0, 0, " `%.*s`", text.length, text.bytes); 157 | } 158 | else if (!length) 159 | { 160 | Env.newline(); 161 | Env.printColor(0, 0, "%.*s", text.length, text.bytes); 162 | } 163 | else 164 | { 165 | Env.newline(); 166 | Env.print("%.*s", length, bytes); 167 | Env.newline(); 168 | 169 | if (length >= text.bytes - bytes) 170 | { 171 | char mark[length + 2]; 172 | long index = 0, marked = 0; 173 | 174 | for (; index < text.bytes - bytes; ++index) 175 | if (isprint(bytes[index])) 176 | mark[index] = ' '; 177 | else 178 | mark[index] = bytes[index]; 179 | 180 | if ((signed char)bytes[index] >= 0) 181 | { 182 | mark[index] = '^'; 183 | marked = 1; 184 | } 185 | else 186 | { 187 | mark[index] = bytes[index]; 188 | if (index > 0) 189 | { 190 | mark[index - 1] = '>'; 191 | marked = 1; 192 | } 193 | } 194 | 195 | while (++index < text.bytes - bytes + text.length && index <= length) 196 | if (isprint(bytes[index]) || isspace(bytes[index])) 197 | mark[index] = '~'; 198 | else 199 | mark[index] = bytes[index]; 200 | 201 | if (!marked) 202 | mark[index++] = '<'; 203 | 204 | mark[index] = '\0'; 205 | 206 | if ((text.bytes - bytes) > 0) 207 | Env.printColor(0, Env(invisible), "%.*s", (text.bytes - bytes), mark); 208 | 209 | Env.printColor(Env(green), Env(bold), "%s", mark + (text.bytes - bytes)); 210 | } 211 | } 212 | 213 | Env.newline(); 214 | } 215 | 216 | int32_t findLine (struct Input *self, struct Text text) 217 | { 218 | uint16_t line = self->lineCount + 1; 219 | while (line--) 220 | if ((self->bytes + self->lines[line] <= text.bytes) && (self->bytes + self->lines[line] < self->bytes + self->length)) 221 | return line; 222 | 223 | return -1; 224 | } 225 | 226 | struct Value attachValue (struct Input *self, struct Value value) 227 | { 228 | if (value.type == Value(charsType)) 229 | value.data.chars->referenceCount++; 230 | 231 | self->attached = realloc(self->attached, sizeof(*self->attached) * (self->attachedCount + 1)); 232 | self->attached[self->attachedCount] = value; 233 | return value; 234 | } 235 | -------------------------------------------------------------------------------- /src/value.h: -------------------------------------------------------------------------------- 1 | // 2 | // value.h 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #ifndef io_libecc_value_h 10 | #ifdef Implementation 11 | #undef Implementation 12 | #include __FILE__ 13 | #include "implementation.h" 14 | #else 15 | #include "interface.h" 16 | #define io_libecc_value_h 17 | 18 | struct Context; 19 | 20 | #include "key.h" 21 | 22 | enum Value(Type) { 23 | 24 | /* Primitive */ 25 | 26 | /* false */ 27 | 28 | /* 1000 0110 */ Value(nullType) = -1 & ~0x79, 29 | /* 1010 0110 */ Value(falseType) = -1 & ~0x59, 30 | /* 0000 0000 */ Value(undefinedType) = 0x00, 31 | 32 | /* computed truth */ 33 | 34 | /* 0000 1000 */ Value(integerType) = 0x08, 35 | /* 0000 1010 */ Value(binaryType) = 0x0a, 36 | 37 | /* 0001 0000 */ Value(keyType) = 0x10, 38 | /* 0001 0010 */ Value(textType) = 0x12, 39 | /* 0001 0011 */ Value(charsType) = 0x13, 40 | /* 0001 0100 */ Value(bufferType) = 0x14, 41 | 42 | /* true */ 43 | 44 | /* 0010 0000 */ Value(trueType) = 0x20, 45 | 46 | /* Objects */ 47 | 48 | /* 0100 0000 */ Value(objectType) = 0x40, 49 | /* 0100 0001 */ Value(errorType) = 0x41, 50 | /* 0100 0010 */ Value(functionType) = 0x42, 51 | /* 0100 0011 */ Value(regexpType) = 0x43, 52 | /* 0100 0100 */ Value(dateType) = 0x44, 53 | /* 0100 1000 */ Value(numberType) = 0x48, 54 | /* 0101 0000 */ Value(stringType) = 0x50, 55 | /* 0110 0000 */ Value(booleanType) = 0x60, 56 | 57 | /* 0100 0110 */ Value(hostType) = 0x46, 58 | /* 0100 0111 */ Value(referenceType) = 0x47, 59 | }; 60 | 61 | enum Value(Mask) { 62 | /* 0000 1000 */ Value(numberMask) = 0x08, 63 | /* 0001 0000 */ Value(stringMask) = 0x10, 64 | /* 0010 0000 */ Value(booleanMask) = 0x20, 65 | /* 0100 0000 */ Value(objectMask) = 0x40, 66 | /* 0100 0001 */ Value(dynamicMask) = 0x41, 67 | }; 68 | 69 | enum Value(Flags) 70 | { 71 | Value(readonly) = 1 << 0, 72 | Value(hidden) = 1 << 1, 73 | Value(sealed) = 1 << 2, 74 | Value(getter) = 1 << 3, 75 | Value(setter) = 1 << 4, 76 | Value(asOwn) = 1 << 5, 77 | Value(asData) = 1 << 6, 78 | 79 | Value(frozen) = Value(readonly) | Value(sealed), 80 | Value(accessor) = Value(getter) | Value(setter), 81 | }; 82 | 83 | enum Value(hintPrimitive) 84 | { 85 | Value(hintAuto) = 0, 86 | Value(hintString) = 1, 87 | Value(hintNumber) = -1, 88 | }; 89 | 90 | extern const struct Value Value(none); 91 | extern const struct Value Value(undefined); 92 | extern const struct Value Value(true); 93 | extern const struct Value Value(false); 94 | extern const struct Value Value(null); 95 | 96 | #endif 97 | 98 | 99 | Interface(Value, 100 | 101 | (struct Value, truth ,(int truth)) 102 | (struct Value, integer ,(int32_t integer)) 103 | (struct Value, binary ,(double binary)) 104 | (struct Value, buffer ,(const char buffer[7], uint8_t units)) 105 | (struct Value, key ,(struct Key key)) 106 | (struct Value, text ,(const struct Text *text)) 107 | (struct Value, chars ,(struct Chars *chars)) 108 | (struct Value, object ,(struct Object *)) 109 | (struct Value, error ,(struct Error *)) 110 | (struct Value, string ,(struct String *)) 111 | (struct Value, regexp ,(struct RegExp *)) 112 | (struct Value, number ,(struct Number *)) 113 | (struct Value, boolean ,(struct Boolean *)) 114 | (struct Value, date ,(struct Date *)) 115 | (struct Value, function ,(struct Function *)) 116 | (struct Value, host ,(struct Object *)) 117 | (struct Value, reference ,(struct Value *)) 118 | 119 | (int, isPrimitive ,(struct Value)) 120 | (int, isBoolean ,(struct Value)) 121 | (int, isNumber ,(struct Value)) 122 | (int, isString ,(struct Value)) 123 | (int, isObject ,(struct Value)) 124 | (int, isDynamic ,(struct Value)) 125 | (int, isTrue ,(struct Value)) 126 | 127 | (struct Value, toPrimitive ,(struct Context * const, struct Value, enum Value(hintPrimitive))) 128 | 129 | (struct Value, toBinary ,(struct Context * const, struct Value)) 130 | (struct Value, toInteger ,(struct Context * const, struct Value)) 131 | (struct Value, binaryToString ,(double binary, int base)) 132 | 133 | (struct Value, toString ,(struct Context * const, struct Value)) 134 | (int32_t, stringLength ,(const struct Value *)) 135 | (const char *, stringBytes ,(const struct Value *)) 136 | (struct Text, textOf ,(const struct Value *string)) 137 | 138 | (struct Value, toObject ,(struct Context * const, struct Value)) 139 | (struct Value, objectValue ,(struct Object *)) 140 | (int, objectIsArray ,(struct Object *)) 141 | 142 | (struct Value, toType ,(struct Value)) 143 | 144 | (struct Value, equals ,(struct Context * const, struct Value, struct Value)) 145 | (struct Value, same ,(struct Context * const, struct Value, struct Value)) 146 | (struct Value, add ,(struct Context * const, struct Value, struct Value)) 147 | (struct Value, subtract ,(struct Context * const, struct Value, struct Value)) 148 | (struct Value, less ,(struct Context * const, struct Value, struct Value)) 149 | (struct Value, more ,(struct Context * const, struct Value, struct Value)) 150 | (struct Value, lessOrEqual ,(struct Context * const, struct Value, struct Value)) 151 | (struct Value, moreOrEqual ,(struct Context * const, struct Value, struct Value)) 152 | 153 | (const char *, typeName ,(enum Value(Type))) 154 | (const char *, maskName ,(enum Value(Mask))) 155 | 156 | (void, dumpTo ,(struct Value, FILE *)) 157 | , 158 | { 159 | union 160 | { 161 | int32_t integer; 162 | double binary; 163 | char buffer[8]; 164 | struct Key key; 165 | const struct Text *text; 166 | 167 | struct Chars *chars; 168 | 169 | struct Object *object; 170 | struct Error *error; 171 | struct String *string; 172 | struct RegExp *regexp; 173 | struct Number *number; 174 | struct Boolean *boolean; 175 | struct Date *date; 176 | struct Function *function; 177 | 178 | struct Value *reference; 179 | } data; 180 | struct Key key; 181 | int8_t type; 182 | uint8_t flags; 183 | uint16_t check; 184 | } 185 | ) 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /src/ecc.c: -------------------------------------------------------------------------------- 1 | // 2 | // ecc.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "ecc.h" 11 | 12 | #include "parser.h" 13 | #include "oplist.h" 14 | #include "pool.h" 15 | 16 | // MARK: - Private 17 | 18 | static int instanceCount = 0; 19 | 20 | // MARK: - Static Members 21 | 22 | static 23 | void addInput(struct Ecc *self, struct Input *input) 24 | { 25 | self->inputs = realloc(self->inputs, sizeof(*self->inputs) * (self->inputCount + 1)); 26 | self->inputs[self->inputCount++] = input; 27 | } 28 | 29 | // MARK: - Methods 30 | 31 | uint32_t Ecc(version) = (0 << 24) | (1 << 16) | (0 << 0); 32 | 33 | struct Ecc *create (void) 34 | { 35 | struct Ecc *self; 36 | 37 | if (!instanceCount++) 38 | { 39 | Env.setup(); 40 | Pool.setup(); 41 | Key.setup(); 42 | Global.setup(); 43 | } 44 | 45 | self = malloc(sizeof(*self)); 46 | *self = Ecc.identity; 47 | 48 | self->global = Global.create(); 49 | self->maximumCallDepth = 512; 50 | 51 | return self; 52 | } 53 | 54 | void destroy (struct Ecc *self) 55 | { 56 | assert(self); 57 | 58 | while (self->inputCount--) 59 | Input.destroy(self->inputs[self->inputCount]), self->inputs[self->inputCount] = NULL; 60 | 61 | free(self->inputs), self->inputs = NULL; 62 | free(self->envList), self->envList = NULL; 63 | free(self), self = NULL; 64 | 65 | if (!--instanceCount) 66 | { 67 | Global.teardown(); 68 | Key.teardown(); 69 | Pool.teardown(); 70 | Env.teardown(); 71 | } 72 | } 73 | 74 | void addFunction (struct Ecc *self, const char *name, const Native(Function) native, int argumentCount, enum Value(Flags) flags) 75 | { 76 | assert(self); 77 | 78 | Function.addFunction(self->global, name, native, argumentCount, flags); 79 | } 80 | 81 | void addValue (struct Ecc *self, const char *name, struct Value value, enum Value(Flags) flags) 82 | { 83 | assert(self); 84 | 85 | Function.addValue(self->global, name, value, flags); 86 | } 87 | 88 | Ecc(useframe) 89 | int evalInput (struct Ecc *self, struct Input *input, enum Ecc(EvalFlags) flags) 90 | { 91 | volatile int result = EXIT_SUCCESS, trap = !self->envCount || flags & Ecc(primitiveResult), catch = 0; 92 | struct Context context = { 93 | .environment = &self->global->environment, 94 | .this = Value.object(&self->global->environment), 95 | .ecc = self, 96 | .strictMode = !(flags & Ecc(sloppyMode)), 97 | }; 98 | 99 | if (!input) 100 | return EXIT_FAILURE; 101 | 102 | self->sloppyMode = flags & Ecc(sloppyMode); 103 | 104 | if (trap) 105 | { 106 | self->printLastThrow = 1; 107 | catch = setjmp(*pushEnv(self)); 108 | } 109 | 110 | if (catch) 111 | result = EXIT_FAILURE; 112 | else 113 | evalInputWithContext(self, input, &context); 114 | 115 | if (flags & Ecc(primitiveResult)) 116 | { 117 | Context.rewindStatement(&context); 118 | context.text = &context.ops->text; 119 | 120 | if ((flags & Ecc(stringResult)) == Ecc(stringResult)) 121 | self->result = Value.toString(&context, self->result); 122 | else 123 | self->result = Value.toPrimitive(&context, self->result, Value(hintAuto)); 124 | } 125 | 126 | if (trap) 127 | { 128 | popEnv(self); 129 | self->printLastThrow = 0; 130 | } 131 | 132 | return result; 133 | } 134 | 135 | void evalInputWithContext (struct Ecc *self, struct Input *input, struct Context *context) 136 | { 137 | struct Lexer *lexer; 138 | struct Parser *parser; 139 | struct Function *function; 140 | 141 | assert(self); 142 | assert(self->envCount); 143 | 144 | addInput(self, input); 145 | 146 | lexer = Lexer.createWithInput(input); 147 | parser = Parser.createWithLexer(lexer); 148 | 149 | if (context->strictMode) 150 | parser->strictMode = 1; 151 | 152 | if (self->sloppyMode) 153 | lexer->allowUnicodeOutsideLiteral = 1; 154 | 155 | function = Parser.parseWithEnvironment(parser, context->environment, &self->global->environment); 156 | context->ops = function->oplist->ops; 157 | context->environment = &function->environment; 158 | 159 | Parser.destroy(parser), parser = NULL; 160 | 161 | // fprintf(stderr, "--- source:\n%.*s\n", input->length, input->bytes); 162 | // OpList.dumpTo(function->oplist, stderr); 163 | 164 | self->result = Value(undefined); 165 | 166 | context->ops->native(context); 167 | } 168 | 169 | jmp_buf * pushEnv(struct Ecc *self) 170 | { 171 | if (self->envCount >= self->envCapacity) 172 | { 173 | uint16_t capacity = self->envCapacity? self->envCapacity * 2: 8; 174 | self->envList = realloc(self->envList, sizeof(*self->envList) * capacity); 175 | memset(self->envList + self->envCapacity, 0, sizeof(*self->envList) * (capacity - self->envCapacity)); 176 | self->envCapacity = capacity; 177 | } 178 | return &self->envList[self->envCount++]; 179 | } 180 | 181 | void popEnv(struct Ecc *self) 182 | { 183 | assert(self->envCount); 184 | 185 | --self->envCount; 186 | } 187 | 188 | void jmpEnv (struct Ecc *self, struct Value value) 189 | { 190 | assert(self); 191 | assert(self->envCount); 192 | 193 | self->result = value; 194 | 195 | if (value.type == Value(errorType)) 196 | self->text = value.data.error->text; 197 | 198 | longjmp(self->envList[self->envCount - 1], 1); 199 | } 200 | 201 | void fatal (const char *format, ...) 202 | { 203 | int16_t length; 204 | va_list ap; 205 | 206 | va_start(ap, format); 207 | length = vsnprintf(NULL, 0, format, ap); 208 | va_end(ap); 209 | { 210 | const char type[] = "Fatal"; 211 | char buffer[length + 1]; 212 | 213 | va_start(ap, format); 214 | vsprintf(buffer, format, ap); 215 | va_end(ap); 216 | 217 | Env.printError(sizeof(type)-1, type, "%s", buffer); 218 | } 219 | 220 | exit(EXIT_FAILURE); 221 | } 222 | 223 | struct Input * findInput (struct Ecc *self, struct Text text) 224 | { 225 | uint16_t i; 226 | 227 | for (i = 0; i < self->inputCount; ++i) 228 | if (text.bytes >= self->inputs[i]->bytes && text.bytes <= self->inputs[i]->bytes + self->inputs[i]->length) 229 | return self->inputs[i]; 230 | 231 | return NULL; 232 | } 233 | 234 | void printTextInput (struct Ecc *self, struct Text text, int fullLine) 235 | { 236 | int32_t ofLine; 237 | struct Text ofText; 238 | const char *ofInput; 239 | 240 | assert(self); 241 | 242 | if (text.bytes >= self->ofText.bytes && text.bytes < self->ofText.bytes + self->ofText.length) 243 | { 244 | ofLine = self->ofLine; 245 | ofText = self->ofText; 246 | ofInput = self->ofInput; 247 | } 248 | else 249 | { 250 | ofLine = 0; 251 | ofText = Text(empty); 252 | ofInput = NULL; 253 | } 254 | 255 | Input.printText(findInput(self, text), text, ofLine, ofText, ofInput, fullLine); 256 | } 257 | 258 | void garbageCollect(struct Ecc *self) 259 | { 260 | uint16_t index, count; 261 | 262 | Pool.unmarkAll(); 263 | Pool.markValue(Value.object(Arguments(prototype))); 264 | Pool.markValue(Value.function(self->global)); 265 | 266 | for (index = 0, count = self->inputCount; index < count; ++index) 267 | { 268 | struct Input *input = self->inputs[index]; 269 | uint16_t a = input->attachedCount; 270 | 271 | while (a--) 272 | Pool.markValue(input->attached[a]); 273 | } 274 | 275 | Pool.collectUnmarked(); 276 | } 277 | -------------------------------------------------------------------------------- /src/builtin/number.c: -------------------------------------------------------------------------------- 1 | // 2 | // number.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "number.h" 11 | 12 | #include "../ecc.h" 13 | #include "../pool.h" 14 | 15 | // MARK: - Private 16 | 17 | struct Object * Number(prototype) = NULL; 18 | struct Function * Number(constructor) = NULL; 19 | 20 | const struct Object(Type) Number(type) = { 21 | .text = &Text(numberType), 22 | }; 23 | 24 | // MARK: - Static Members 25 | 26 | static 27 | struct Value toExponential (struct Context * const context) 28 | { 29 | struct Chars(Append) chars; 30 | struct Value value; 31 | double binary, precision = 0; 32 | 33 | Context.assertThisMask(context, Value(numberMask)); 34 | 35 | binary = Value.toBinary(context, context->this).data.binary; 36 | value = Context.argument(context, 0); 37 | if (value.type != Value(undefinedType)) 38 | { 39 | precision = Value.toBinary(context, value).data.binary; 40 | if (precision <= -1 || precision >= 21) 41 | Context.rangeError(context, Chars.create("precision '%.0f' out of range", precision)); 42 | 43 | if (isnan(precision)) 44 | precision = 0; 45 | } 46 | 47 | if (isnan(binary)) 48 | return Value.text(&Text(nan)); 49 | else if (binary == INFINITY) 50 | return Value.text(&Text(infinity)); 51 | else if (binary == -INFINITY) 52 | return Value.text(&Text(negativeInfinity)); 53 | 54 | Chars.beginAppend(&chars); 55 | 56 | if (value.type != Value(undefinedType)) 57 | Chars.append(&chars, "%.*e", (int32_t)precision, binary); 58 | else 59 | Chars.append(&chars, "%e", binary); 60 | 61 | Chars.normalizeBinary(&chars); 62 | return Chars.endAppend(&chars); 63 | } 64 | 65 | static 66 | struct Value toFixed (struct Context * const context) 67 | { 68 | struct Chars(Append) chars; 69 | struct Value value; 70 | double binary, precision = 0; 71 | 72 | Context.assertThisMask(context, Value(numberMask)); 73 | 74 | binary = Value.toBinary(context, context->this).data.binary; 75 | value = Context.argument(context, 0); 76 | if (value.type != Value(undefinedType)) 77 | { 78 | precision = Value.toBinary(context, value).data.binary; 79 | if (precision <= -1 || precision >= 21) 80 | Context.rangeError(context, Chars.create("precision '%.0f' out of range", precision)); 81 | 82 | if (isnan(precision)) 83 | precision = 0; 84 | } 85 | 86 | if (isnan(binary)) 87 | return Value.text(&Text(nan)); 88 | else if (binary == INFINITY) 89 | return Value.text(&Text(infinity)); 90 | else if (binary == -INFINITY) 91 | return Value.text(&Text(negativeInfinity)); 92 | 93 | Chars.beginAppend(&chars); 94 | 95 | if (binary <= -1e+21 || binary >= 1e+21) 96 | Chars.appendBinary(&chars, binary, 10); 97 | else 98 | Chars.append(&chars, "%.*f", (int32_t)precision, binary); 99 | 100 | return Chars.endAppend(&chars); 101 | } 102 | 103 | static 104 | struct Value toPrecision (struct Context * const context) 105 | { 106 | struct Chars(Append) chars; 107 | struct Value value; 108 | double binary, precision = 0; 109 | 110 | Context.assertThisMask(context, Value(numberMask)); 111 | 112 | binary = Value.toBinary(context, context->this).data.binary; 113 | value = Context.argument(context, 0); 114 | if (value.type != Value(undefinedType)) 115 | { 116 | precision = Value.toBinary(context, value).data.binary; 117 | if (precision <= -1 || precision >= 101) 118 | Context.rangeError(context, Chars.create("precision '%.0f' out of range", precision)); 119 | 120 | if (isnan(precision)) 121 | precision = 0; 122 | } 123 | 124 | if (isnan(binary)) 125 | return Value.text(&Text(nan)); 126 | else if (binary == INFINITY) 127 | return Value.text(&Text(infinity)); 128 | else if (binary == -INFINITY) 129 | return Value.text(&Text(negativeInfinity)); 130 | 131 | Chars.beginAppend(&chars); 132 | 133 | if (value.type != Value(undefinedType)) 134 | { 135 | Chars.append(&chars, "%.*g", (int32_t)precision, binary); 136 | Chars.normalizeBinary(&chars); 137 | } 138 | else 139 | Chars.appendBinary(&chars, binary, 10); 140 | 141 | return Chars.endAppend(&chars); 142 | } 143 | 144 | static 145 | struct Value toString (struct Context * const context) 146 | { 147 | struct Value value; 148 | int32_t radix = 10; 149 | double binary; 150 | 151 | Context.assertThisMask(context, Value(numberMask)); 152 | 153 | binary = Value.toBinary(context, context->this).data.binary; 154 | value = Context.argument(context, 0); 155 | if (value.type != Value(undefinedType)) 156 | { 157 | radix = Value.toInteger(context, value).data.integer; 158 | if (radix < 2 || radix > 36) 159 | Context.rangeError(context, Chars.create("radix must be an integer at least 2 and no greater than 36")); 160 | 161 | if (radix != 10 && (binary < LONG_MIN || binary > LONG_MAX)) 162 | Env.printWarning("%g.toString(%d) out of bounds; only long int are supported by radices other than 10", binary, radix); 163 | } 164 | 165 | return Value.binaryToString(binary, radix); 166 | } 167 | 168 | static 169 | struct Value valueOf (struct Context * const context) 170 | { 171 | Context.assertThisType(context, Value(numberType)); 172 | 173 | return Value.binary(context->this.data.number->value); 174 | } 175 | 176 | static 177 | struct Value constructor (struct Context * const context) 178 | { 179 | struct Value value; 180 | 181 | value = Context.argument(context, 0); 182 | if (value.type == Value(undefinedType)) 183 | value = Value.binary(value.check == 1? NAN: 0); 184 | else 185 | value = Value.toBinary(context, value); 186 | 187 | if (context->construct) 188 | return Value.number(Number.create(value.data.binary)); 189 | else 190 | return value; 191 | } 192 | 193 | // MARK: - Methods 194 | 195 | void setup () 196 | { 197 | const enum Value(Flags) r = Value(readonly); 198 | const enum Value(Flags) h = Value(hidden); 199 | const enum Value(Flags) s = Value(sealed); 200 | 201 | Function.setupBuiltinObject( 202 | &Number(constructor), constructor, 1, 203 | &Number(prototype), Value.number(create(0)), 204 | &Number(type)); 205 | 206 | Function.addMember(Number(constructor), "MAX_VALUE", Value.binary(DBL_MAX), r|h|s); 207 | Function.addMember(Number(constructor), "MIN_VALUE", Value.binary(DBL_MIN * DBL_EPSILON), r|h|s); 208 | Function.addMember(Number(constructor), "NaN", Value.binary(NAN), r|h|s); 209 | Function.addMember(Number(constructor), "NEGATIVE_INFINITY", Value.binary(-INFINITY), r|h|s); 210 | Function.addMember(Number(constructor), "POSITIVE_INFINITY", Value.binary(INFINITY), r|h|s); 211 | 212 | Function.addToObject(Number(prototype), "toString", toString, 1, h); 213 | Function.addToObject(Number(prototype), "toLocaleString", toString, 1, h); 214 | Function.addToObject(Number(prototype), "valueOf", valueOf, 0, h); 215 | Function.addToObject(Number(prototype), "toFixed", toFixed, 1, h); 216 | Function.addToObject(Number(prototype), "toExponential", toExponential, 1, h); 217 | Function.addToObject(Number(prototype), "toPrecision", toPrecision, 1, h); 218 | } 219 | 220 | void teardown (void) 221 | { 222 | Number(prototype) = NULL; 223 | Number(constructor) = NULL; 224 | } 225 | 226 | struct Number * create (double binary) 227 | { 228 | struct Number *self = malloc(sizeof(*self)); 229 | *self = Number.identity; 230 | Pool.addObject(&self->object); 231 | Object.initialize(&self->object, Number(prototype)); 232 | 233 | self->value = binary; 234 | 235 | return self; 236 | } 237 | -------------------------------------------------------------------------------- /src/builtin/error.c: -------------------------------------------------------------------------------- 1 | // 2 | // error.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "error.h" 11 | 12 | #include "../pool.h" 13 | 14 | // MARK: - Private 15 | 16 | struct Object * Error(prototype) = NULL; 17 | struct Object * Error(rangePrototype) = NULL; 18 | struct Object * Error(referencePrototype) = NULL; 19 | struct Object * Error(syntaxPrototype) = NULL; 20 | struct Object * Error(typePrototype) = NULL; 21 | struct Object * Error(uriPrototype) = NULL; 22 | struct Object * Error(evalPrototype) = NULL; 23 | 24 | struct Function * Error(constructor) = NULL; 25 | struct Function * Error(rangeConstructor) = NULL; 26 | struct Function * Error(referenceConstructor) = NULL; 27 | struct Function * Error(syntaxConstructor) = NULL; 28 | struct Function * Error(typeConstructor) = NULL; 29 | struct Function * Error(uriConstructor) = NULL; 30 | struct Function * Error(evalConstructor) = NULL; 31 | 32 | const struct Object(Type) Error(type) = { 33 | .text = &Text(errorType), 34 | }; 35 | 36 | // MARK: - Static Members 37 | 38 | static struct Error * evalError (struct Text text, struct Chars *message); 39 | 40 | static 41 | struct Chars *messageValue (struct Context * const context, struct Value value) 42 | { 43 | if (value.type == Value(undefinedType)) 44 | return NULL; 45 | else if (value.type == Value(charsType)) 46 | return value.data.chars; 47 | else 48 | { 49 | value = Value.toString(context, value); 50 | return Chars.create("%.*s", Value.stringLength(&value), Value.stringBytes(&value)); 51 | } 52 | } 53 | 54 | static 55 | struct Value toChars (struct Context * const context, struct Value value) 56 | { 57 | struct Value name, message; 58 | struct Object *self; 59 | struct Chars(Append) chars; 60 | 61 | assert(value.type == Value(errorType)); 62 | assert(value.data.error); 63 | 64 | self = value.data.object; 65 | 66 | name = Object.getMember(context, self, Key(name)); 67 | if (name.type == Value(undefinedType)) 68 | name = Value.text(&Text(errorName)); 69 | else 70 | name = Value.toString(context, name); 71 | 72 | message = Object.getMember(context, self, Key(message)); 73 | if (message.type == Value(undefinedType)) 74 | message = Value.text(&Text(empty)); 75 | else 76 | message = Value.toString(context, message); 77 | 78 | Chars.beginAppend(&chars); 79 | Chars.appendValue(&chars, context, name); 80 | 81 | if (Value.stringLength(&name) && Value.stringLength(&message)) 82 | Chars.append(&chars, ": "); 83 | 84 | Chars.appendValue(&chars, context, message); 85 | 86 | return Chars.endAppend(&chars); 87 | } 88 | 89 | static 90 | struct Error * create (struct Object *errorPrototype, struct Text text, struct Chars *message) 91 | { 92 | struct Error *self = malloc(sizeof(*self)); 93 | Pool.addObject(&self->object); 94 | 95 | *self = Error.identity; 96 | 97 | Object.initialize(&self->object, errorPrototype); 98 | 99 | self->text = text; 100 | 101 | if (message) 102 | { 103 | Object.addMember(&self->object, Key(message), Value.chars(message), Value(hidden)); 104 | ++message->referenceCount; 105 | } 106 | 107 | return self; 108 | } 109 | 110 | static 111 | struct Value toString (struct Context * const context) 112 | { 113 | Context.assertThisMask(context, Value(objectMask)); 114 | 115 | return toChars(context, context->this); 116 | } 117 | 118 | static 119 | struct Value errorConstructor (struct Context * const context) 120 | { 121 | struct Chars *message; 122 | 123 | message = messageValue(context, Context.argument(context, 0)); 124 | Context.setTextIndex(context, Context(callIndex)); 125 | return Value.error(error(Context.textSeek(context), message)); 126 | } 127 | 128 | static 129 | struct Value rangeErrorConstructor (struct Context * const context) 130 | { 131 | struct Chars *message; 132 | 133 | message = messageValue(context, Context.argument(context, 0)); 134 | Context.setTextIndex(context, Context(callIndex)); 135 | return Value.error(rangeError(Context.textSeek(context), message)); 136 | } 137 | 138 | static 139 | struct Value referenceErrorConstructor (struct Context * const context) 140 | { 141 | struct Chars *message; 142 | 143 | message = messageValue(context, Context.argument(context, 0)); 144 | Context.setTextIndex(context, Context(callIndex)); 145 | return Value.error(referenceError(Context.textSeek(context), message)); 146 | } 147 | 148 | static 149 | struct Value syntaxErrorConstructor (struct Context * const context) 150 | { 151 | struct Chars *message; 152 | 153 | message = messageValue(context, Context.argument(context, 0)); 154 | Context.setTextIndex(context, Context(callIndex)); 155 | return Value.error(syntaxError(Context.textSeek(context), message)); 156 | } 157 | 158 | static 159 | struct Value typeErrorConstructor (struct Context * const context) 160 | { 161 | struct Chars *message; 162 | 163 | message = messageValue(context, Context.argument(context, 0)); 164 | Context.setTextIndex(context, Context(callIndex)); 165 | return Value.error(typeError(Context.textSeek(context), message)); 166 | } 167 | 168 | static 169 | struct Value uriErrorConstructor (struct Context * const context) 170 | { 171 | struct Chars *message; 172 | 173 | message = messageValue(context, Context.argument(context, 0)); 174 | Context.setTextIndex(context, Context(callIndex)); 175 | return Value.error(uriError(Context.textSeek(context), message)); 176 | } 177 | 178 | static 179 | struct Value evalErrorConstructor (struct Context * const context) 180 | { 181 | struct Chars *message; 182 | 183 | message = messageValue(context, Context.argument(context, 0)); 184 | Context.setTextIndex(context, Context(callIndex)); 185 | return Value.error(evalError(Context.textSeek(context), message)); 186 | } 187 | 188 | static 189 | void setupBuiltinObject (struct Function **constructor, const Native(Function) native, int parameterCount, struct Object **prototype, const struct Text *name) 190 | { 191 | Function.setupBuiltinObject( 192 | constructor, native, 1, 193 | prototype, Value.error(error(*name, NULL)), 194 | &Error(type)); 195 | 196 | Object.addMember(*prototype, Key(name), Value.text(name), Value(hidden)); 197 | } 198 | 199 | // MARK: - Methods 200 | 201 | void setup (void) 202 | { 203 | const enum Value(Flags) h = Value(hidden); 204 | 205 | setupBuiltinObject(&Error(constructor), errorConstructor, 1, &Error(prototype), &Text(errorName)); 206 | setupBuiltinObject(&Error(rangeConstructor), rangeErrorConstructor, 1, &Error(rangePrototype), &Text(rangeErrorName)); 207 | setupBuiltinObject(&Error(referenceConstructor), referenceErrorConstructor, 1, &Error(referencePrototype), &Text(referenceErrorName)); 208 | setupBuiltinObject(&Error(syntaxConstructor), syntaxErrorConstructor, 1, &Error(syntaxPrototype), &Text(syntaxErrorName)); 209 | setupBuiltinObject(&Error(typeConstructor), typeErrorConstructor, 1, &Error(typePrototype), &Text(typeErrorName)); 210 | setupBuiltinObject(&Error(uriConstructor), uriErrorConstructor, 1, &Error(uriPrototype), &Text(uriErrorName)); 211 | setupBuiltinObject(&Error(evalConstructor), evalErrorConstructor, 1, &Error(evalPrototype), &Text(evalErrorName)); 212 | 213 | Function.addToObject(Error(prototype), "toString", toString, 0, h); 214 | 215 | Object.addMember(Error(prototype), Key(message), Value.text(&Text(empty)), h); 216 | } 217 | 218 | void teardown (void) 219 | { 220 | Error(prototype) = NULL; 221 | Error(constructor) = NULL; 222 | 223 | Error(rangePrototype) = NULL; 224 | Error(rangeConstructor) = NULL; 225 | 226 | Error(referencePrototype) = NULL; 227 | Error(referenceConstructor) = NULL; 228 | 229 | Error(syntaxPrototype) = NULL; 230 | Error(syntaxConstructor) = NULL; 231 | 232 | Error(typePrototype) = NULL; 233 | Error(typeConstructor) = NULL; 234 | 235 | Error(uriPrototype) = NULL; 236 | Error(uriConstructor) = NULL; 237 | } 238 | 239 | struct Error * error (struct Text text, struct Chars *message) 240 | { 241 | return create(Error(prototype), text, message); 242 | } 243 | 244 | struct Error * rangeError (struct Text text, struct Chars *message) 245 | { 246 | return create(Error(rangePrototype), text, message); 247 | } 248 | 249 | struct Error * referenceError (struct Text text, struct Chars *message) 250 | { 251 | return create(Error(referencePrototype), text, message); 252 | } 253 | 254 | struct Error * syntaxError (struct Text text, struct Chars *message) 255 | { 256 | return create(Error(syntaxPrototype), text, message); 257 | } 258 | 259 | struct Error * typeError (struct Text text, struct Chars *message) 260 | { 261 | return create(Error(typePrototype), text, message); 262 | } 263 | 264 | struct Error * uriError (struct Text text, struct Chars *message) 265 | { 266 | return create(Error(uriPrototype), text, message); 267 | } 268 | 269 | struct Error * evalError (struct Text text, struct Chars *message) 270 | { 271 | return create(Error(evalPrototype), text, message); 272 | } 273 | 274 | void destroy (struct Error *self) 275 | { 276 | assert(self); 277 | if (!self) 278 | return; 279 | 280 | Object.finalize(&self->object); 281 | 282 | free(self), self = NULL; 283 | } 284 | -------------------------------------------------------------------------------- /src/builtin/math.c: -------------------------------------------------------------------------------- 1 | // 2 | // math.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "math.h" 11 | 12 | // MARK: - Private 13 | 14 | const struct Object(Type) Math(type) = { 15 | .text = &Text(mathType), 16 | }; 17 | 18 | // MARK: - Static Members 19 | 20 | static 21 | struct Value mathAbs (struct Context * const context) 22 | { 23 | struct Value value; 24 | 25 | value = Context.argument(context, 0); 26 | if (value.type != Value(binaryType)) 27 | value = Value.toBinary(context, value); 28 | 29 | return Value.binary(fabs(value.data.binary)); 30 | } 31 | 32 | static 33 | struct Value mathACos (struct Context * const context) 34 | { 35 | struct Value value; 36 | 37 | value = Context.argument(context, 0); 38 | if (value.type != Value(binaryType)) 39 | value = Value.toBinary(context, value); 40 | 41 | return Value.binary(acos(value.data.binary)); 42 | } 43 | 44 | static 45 | struct Value mathASin (struct Context * const context) 46 | { 47 | struct Value value; 48 | 49 | value = Context.argument(context, 0); 50 | if (value.type != Value(binaryType)) 51 | value = Value.toBinary(context, value); 52 | 53 | return Value.binary(asin(value.data.binary)); 54 | } 55 | 56 | static 57 | struct Value mathATan (struct Context * const context) 58 | { 59 | struct Value value; 60 | 61 | value = Context.argument(context, 0); 62 | if (value.type != Value(binaryType)) 63 | value = Value.toBinary(context, value); 64 | 65 | return Value.binary(atan(value.data.binary)); 66 | } 67 | 68 | static 69 | struct Value mathATan2 (struct Context * const context) 70 | { 71 | struct Value x, y; 72 | 73 | x = Context.argument(context, 0); 74 | if (x.type != Value(binaryType)) 75 | x = Value.toBinary(context, x); 76 | 77 | y = Context.argument(context, 1); 78 | if (y.type != Value(binaryType)) 79 | y = Value.toBinary(context, y); 80 | 81 | return Value.binary(atan2(x.data.binary, y.data.binary)); 82 | } 83 | 84 | static 85 | struct Value mathCeil (struct Context * const context) 86 | { 87 | struct Value value; 88 | 89 | value = Context.argument(context, 0); 90 | if (value.type != Value(binaryType)) 91 | value = Value.toBinary(context, value); 92 | 93 | return Value.binary(ceil(value.data.binary)); 94 | } 95 | 96 | static 97 | struct Value mathCos (struct Context * const context) 98 | { 99 | struct Value value; 100 | 101 | value = Context.argument(context, 0); 102 | if (value.type != Value(binaryType)) 103 | value = Value.toBinary(context, value); 104 | 105 | return Value.binary(cos(value.data.binary)); 106 | } 107 | 108 | static 109 | struct Value mathExp (struct Context * const context) 110 | { 111 | struct Value value; 112 | 113 | value = Context.argument(context, 0); 114 | if (value.type != Value(binaryType)) 115 | value = Value.toBinary(context, value); 116 | 117 | return Value.binary(exp(value.data.binary)); 118 | } 119 | 120 | static 121 | struct Value mathFloor (struct Context * const context) 122 | { 123 | struct Value value; 124 | 125 | value = Context.argument(context, 0); 126 | if (value.type != Value(binaryType)) 127 | value = Value.toBinary(context, value); 128 | 129 | return Value.binary(floor(value.data.binary)); 130 | } 131 | 132 | static 133 | struct Value mathLog (struct Context * const context) 134 | { 135 | struct Value value; 136 | 137 | value = Context.argument(context, 0); 138 | if (value.type != Value(binaryType)) 139 | value = Value.toBinary(context, value); 140 | 141 | return Value.binary(log(value.data.binary)); 142 | } 143 | 144 | static 145 | struct Value mathMax (struct Context * const context) 146 | { 147 | double result = -INFINITY, value; 148 | int index, count; 149 | 150 | count = Context.argumentCount(context); 151 | if (!count) 152 | return Value.binary(-INFINITY); 153 | 154 | for (index = 0; index < count; ++index) 155 | { 156 | value = Value.toBinary(context, Context.argument(context, index)).data.binary; 157 | if (isnan(value)) 158 | return Value.binary(NAN); 159 | 160 | if (result < value) 161 | result = value; 162 | } 163 | 164 | return Value.binary(result); 165 | } 166 | 167 | static 168 | struct Value mathMin (struct Context * const context) 169 | { 170 | double result = INFINITY, value; 171 | int index, count; 172 | 173 | count = Context.argumentCount(context); 174 | if (!count) 175 | return Value.binary(INFINITY); 176 | 177 | for (index = 0; index < count; ++index) 178 | { 179 | value = Value.toBinary(context, Context.argument(context, index)).data.binary; 180 | if (isnan(value)) 181 | return Value.binary(NAN); 182 | 183 | if (result > value) 184 | result = value; 185 | } 186 | 187 | return Value.binary(result); 188 | } 189 | 190 | static 191 | struct Value mathPow (struct Context * const context) 192 | { 193 | struct Value x, y; 194 | 195 | x = Value.toBinary(context, Context.argument(context, 0)); 196 | y = Value.toBinary(context, Context.argument(context, 1)); 197 | 198 | if (fabs(x.data.binary) == 1 && !isfinite(y.data.binary)) 199 | return Value.binary(NAN); 200 | 201 | return Value.binary(pow(x.data.binary, y.data.binary)); 202 | } 203 | 204 | static 205 | struct Value mathRandom (struct Context * const context) 206 | { 207 | return Value.binary((double)rand() / (double)RAND_MAX); 208 | } 209 | 210 | static 211 | struct Value mathRound (struct Context * const context) 212 | { 213 | struct Value value; 214 | 215 | value = Context.argument(context, 0); 216 | if (value.type != Value(binaryType)) 217 | value = Value.toBinary(context, value); 218 | 219 | if (value.data.binary < 0) 220 | return Value.binary(1.0 - ceil(0.5 - value.data.binary)); 221 | else 222 | return Value.binary(floor(0.5 + value.data.binary)); 223 | } 224 | 225 | static 226 | struct Value mathSin (struct Context * const context) 227 | { 228 | struct Value value; 229 | 230 | value = Context.argument(context, 0); 231 | if (value.type != Value(binaryType)) 232 | value = Value.toBinary(context, value); 233 | 234 | return Value.binary(sin(value.data.binary)); 235 | } 236 | 237 | static 238 | struct Value mathSqrt (struct Context * const context) 239 | { 240 | struct Value value; 241 | 242 | value = Context.argument(context, 0); 243 | if (value.type != Value(binaryType)) 244 | value = Value.toBinary(context, value); 245 | 246 | return Value.binary(sqrt(value.data.binary)); 247 | } 248 | 249 | static 250 | struct Value mathTan (struct Context * const context) 251 | { 252 | struct Value value; 253 | 254 | value = Context.argument(context, 0); 255 | if (value.type != Value(binaryType)) 256 | value = Value.toBinary(context, value); 257 | 258 | return Value.binary(tan(value.data.binary)); 259 | } 260 | 261 | // MARK: - Public 262 | 263 | struct Object * Math(object) = NULL; 264 | 265 | void setup () 266 | { 267 | const enum Value(Flags) r = Value(readonly); 268 | const enum Value(Flags) h = Value(hidden); 269 | const enum Value(Flags) s = Value(sealed); 270 | 271 | Math(object) = Object.createTyped(&Math(type)); 272 | 273 | Object.addMember(Math(object), Key.makeWithCString("E"), Value.binary(2.71828182845904523536), r|h|s); 274 | Object.addMember(Math(object), Key.makeWithCString("LN10"), Value.binary(2.30258509299404568402), r|h|s); 275 | Object.addMember(Math(object), Key.makeWithCString("LN2"), Value.binary(0.693147180559945309417), r|h|s); 276 | Object.addMember(Math(object), Key.makeWithCString("LOG2E"), Value.binary(1.44269504088896340736), r|h|s); 277 | Object.addMember(Math(object), Key.makeWithCString("LOG10E"), Value.binary(0.434294481903251827651), r|h|s); 278 | Object.addMember(Math(object), Key.makeWithCString("PI"), Value.binary(3.14159265358979323846), r|h|s); 279 | Object.addMember(Math(object), Key.makeWithCString("SQRT1_2"), Value.binary(0.707106781186547524401), r|h|s); 280 | Object.addMember(Math(object), Key.makeWithCString("SQRT2"), Value.binary(1.41421356237309504880), r|h|s); 281 | 282 | Function.addToObject(Math(object), "abs", mathAbs, 1, h); 283 | Function.addToObject(Math(object), "acos", mathACos, 1, h); 284 | Function.addToObject(Math(object), "asin", mathASin, 1, h); 285 | Function.addToObject(Math(object), "atan", mathATan, 1, h); 286 | Function.addToObject(Math(object), "atan2", mathATan2, 2, h); 287 | Function.addToObject(Math(object), "ceil", mathCeil, 1, h); 288 | Function.addToObject(Math(object), "cos", mathCos, 1, h); 289 | Function.addToObject(Math(object), "exp", mathExp, 1, h); 290 | Function.addToObject(Math(object), "floor", mathFloor, 1, h); 291 | Function.addToObject(Math(object), "log", mathLog, 1, h); 292 | Function.addToObject(Math(object), "max", mathMax, -2, h); 293 | Function.addToObject(Math(object), "min", mathMin, -2, h); 294 | Function.addToObject(Math(object), "pow", mathPow, 2, h); 295 | Function.addToObject(Math(object), "random", mathRandom, 0, h); 296 | Function.addToObject(Math(object), "round", mathRound, 1, h); 297 | Function.addToObject(Math(object), "sin", mathSin, 1, h); 298 | Function.addToObject(Math(object), "sqrt", mathSqrt, 1, h); 299 | Function.addToObject(Math(object), "tan", mathTan, 1, h); 300 | } 301 | 302 | void teardown (void) 303 | { 304 | Math(object) = NULL; 305 | } 306 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | // 2 | // context.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "context.h" 11 | 12 | #include "ecc.h" 13 | #include "op.h" 14 | 15 | // MARK: - Private 16 | 17 | // MARK: - Static Members 18 | 19 | // MARK: - Methods 20 | 21 | void rangeError (struct Context * const self, struct Chars *chars) 22 | { 23 | throw(self, Value.error(Error.rangeError(textSeek(self), chars))); 24 | } 25 | 26 | void referenceError (struct Context * const self, struct Chars *chars) 27 | { 28 | throw(self, Value.error(Error.referenceError(textSeek(self), chars))); 29 | } 30 | 31 | void syntaxError (struct Context * const self, struct Chars *chars) 32 | { 33 | throw(self, Value.error(Error.syntaxError(textSeek(self), chars))); 34 | } 35 | 36 | void typeError (struct Context * const self, struct Chars *chars) 37 | { 38 | throw(self, Value.error(Error.typeError(textSeek(self), chars))); 39 | } 40 | 41 | void uriError (struct Context * const self, struct Chars *chars) 42 | { 43 | throw(self, Value.error(Error.uriError(textSeek(self), chars))); 44 | } 45 | 46 | void throw (struct Context * const self, struct Value value) 47 | { 48 | if (value.type == Value(errorType)) 49 | self->ecc->text = value.data.error->text; 50 | 51 | if (self->ecc->printLastThrow && self->ecc->envCount == 1) 52 | { 53 | struct Value name, message; 54 | name = Value(undefined); 55 | 56 | if (value.type == Value(errorType)) 57 | { 58 | name = Value.toString(self, Object.getMember(self, value.data.object, Key(name))); 59 | message = Value.toString(self, Object.getMember(self, value.data.object, Key(message))); 60 | } 61 | else 62 | message = Value.toString(self, value); 63 | 64 | if (name.type == Value(undefinedType)) 65 | name = Value.text(&Text(errorName)); 66 | 67 | Env.newline(); 68 | Env.printError(Value.stringLength(&name), Value.stringBytes(&name), "%.*s" , Value.stringLength(&message), Value.stringBytes(&message)); 69 | printBacktrace(self); 70 | Ecc.printTextInput(self->ecc, self->ecc->text, 1); 71 | } 72 | 73 | Ecc.jmpEnv(self->ecc, value); 74 | } 75 | 76 | struct Value callFunction (struct Context * const self, struct Function *function, struct Value this, int argumentCount, ... ) 77 | { 78 | struct Value result; 79 | va_list ap; 80 | int offset = 0; 81 | 82 | if (argumentCount & Context(asAccessor)) { 83 | offset = Context(accessorOffset); 84 | } 85 | 86 | va_start(ap, argumentCount); 87 | result = Op.callFunctionVA(self, offset, function, this, argumentCount & Context(countMask), ap); 88 | va_end(ap); 89 | 90 | return result; 91 | } 92 | 93 | int argumentCount (struct Context * const self) 94 | { 95 | if (self->environment->hashmap[2].value.type == Value(objectType)) 96 | return self->environment->hashmap[2].value.data.object->elementCount; 97 | else 98 | return self->environment->hashmapCount - 3; 99 | } 100 | 101 | struct Value argument (struct Context * const self, int argumentIndex) 102 | { 103 | self->textIndex = argumentIndex + 4; 104 | 105 | if (self->environment->hashmap[2].value.type == Value(objectType)) 106 | { 107 | if (argumentIndex < self->environment->hashmap[2].value.data.object->elementCount) 108 | return self->environment->hashmap[2].value.data.object->element[argumentIndex].value; 109 | } 110 | else if (argumentIndex < self->environment->hashmapCount - 3) 111 | return self->environment->hashmap[argumentIndex + 3].value; 112 | 113 | return Value(none); 114 | } 115 | 116 | void replaceArgument (struct Context * const self, int argumentIndex, struct Value value) 117 | { 118 | if (self->environment->hashmap[2].value.type == Value(objectType)) 119 | { 120 | if (argumentIndex < self->environment->hashmap[2].value.data.object->elementCount) 121 | self->environment->hashmap[2].value.data.object->element[argumentIndex].value = value; 122 | } 123 | else if (argumentIndex < self->environment->hashmapCount - 3) 124 | self->environment->hashmap[argumentIndex + 3].value = value; 125 | } 126 | 127 | struct Value this (struct Context * const self) 128 | { 129 | self->textIndex = Context(thisIndex); 130 | return self->this; 131 | } 132 | 133 | void assertThisType (struct Context * const self, enum Value(Type) type) 134 | { 135 | if (self->this.type != type) 136 | { 137 | setTextIndex(self, Context(thisIndex)); 138 | typeError(self, Chars.create("'this' is not a %s", Value.typeName(type))); 139 | } 140 | } 141 | 142 | void assertThisMask (struct Context * const self, enum Value(Mask) mask) 143 | { 144 | if (!(self->this.type & mask)) 145 | { 146 | setTextIndex(self, Context(thisIndex)); 147 | typeError(self, Chars.create("'this' is not a %s", Value.maskName(mask))); 148 | } 149 | } 150 | 151 | void assertThisCoerciblePrimitive (struct Context * const self) 152 | { 153 | if (self->this.type == Value(undefinedType) || self->this.type == Value(nullType)) 154 | { 155 | setTextIndex(self, Context(thisIndex)); 156 | typeError(self, Chars.create("'this' cannot be null or undefined")); 157 | } 158 | } 159 | 160 | void setText (struct Context * const self, const struct Text *text) 161 | { 162 | self->textIndex = Context(savedIndex); 163 | self->text = text; 164 | } 165 | 166 | void setTexts (struct Context * const self, const struct Text *text, const struct Text *textAlt) 167 | { 168 | self->textIndex = Context(savedIndex); 169 | self->text = text; 170 | self->textAlt = textAlt; 171 | } 172 | 173 | void setTextIndex (struct Context * const self, enum Context(Index) index) 174 | { 175 | self->textIndex = index; 176 | } 177 | 178 | void setTextIndexArgument (struct Context * const self, int argument) 179 | { 180 | self->textIndex = argument + 4; 181 | } 182 | 183 | struct Text textSeek (struct Context * const self) 184 | { 185 | const char *bytes; 186 | struct Context seek = *self; 187 | uint32_t breakArray = 0, argumentCount = 0; 188 | struct Text callText; 189 | enum Context(Index) index; 190 | int isAccessor = 0; 191 | 192 | assert(self); 193 | assert(self->ecc); 194 | 195 | index = self->textIndex; 196 | 197 | if (index == Context(savedIndex)) 198 | return *self->text; 199 | 200 | if (index == Context(savedIndexAlt)) 201 | return *self->textAlt; 202 | 203 | while (seek.ops->text.bytes == Text(nativeCode).bytes) 204 | { 205 | if (!seek.parent) 206 | return seek.ops->text; 207 | 208 | isAccessor = seek.argumentOffset == Context(accessorOffset); 209 | 210 | if (seek.argumentOffset > 0 && index >= Context(thisIndex)) 211 | { 212 | ++index; 213 | ++argumentCount; 214 | breakArray <<= 1; 215 | 216 | if (seek.argumentOffset == Context(applyOffset)) 217 | breakArray |= 2; 218 | } 219 | seek = *seek.parent; 220 | } 221 | 222 | if (seek.ops->native == Op.noop) 223 | --seek.ops; 224 | 225 | if (isAccessor) 226 | { 227 | if (index > Context(thisIndex)) 228 | Context.rewindStatement(&seek); 229 | } 230 | else if (index > Context(noIndex)) 231 | { 232 | while (seek.ops->text.bytes != seek.textCall->bytes 233 | || seek.ops->text.length != seek.textCall->length 234 | ) 235 | --seek.ops; 236 | 237 | argumentCount += seek.ops->value.data.integer; 238 | callText = seek.ops->text; 239 | 240 | // func 241 | if (index-- > Context(callIndex)) 242 | ++seek.ops; 243 | 244 | // this 245 | if (index-- > Context(callIndex) && (seek.ops + 1)->text.bytes <= seek.ops->text.bytes) 246 | ++seek.ops; 247 | 248 | // arguments 249 | while (index-- > Context(callIndex)) 250 | { 251 | if (!argumentCount--) 252 | return Text.make(callText.bytes + callText.length - 1, 0); 253 | 254 | bytes = seek.ops->text.bytes + seek.ops->text.length; 255 | while (bytes > seek.ops->text.bytes && seek.ops->text.bytes) 256 | ++seek.ops; 257 | 258 | if (breakArray & 0x1 && seek.ops->native == Op.array) 259 | ++seek.ops; 260 | 261 | breakArray >>= 1; 262 | } 263 | } 264 | 265 | return seek.ops->text; 266 | } 267 | 268 | void rewindStatement(struct Context * const context) 269 | { 270 | while (!(context->ops->text.flags & Text(breakFlag))) 271 | --context->ops; 272 | } 273 | 274 | void printBacktrace (struct Context * const context) 275 | { 276 | int depth = context->depth, count, skip; 277 | struct Context frame; 278 | 279 | if (depth > 12) 280 | { 281 | Env.printColor(0, Env(bold), "..."); 282 | fprintf(stderr, " (%d more)\n", depth - 12); 283 | depth = 12; 284 | } 285 | 286 | while (depth > 0) 287 | { 288 | count = depth--; 289 | frame = *context; 290 | skip = 0; 291 | 292 | while (count--) 293 | { 294 | --skip; 295 | 296 | if (frame.argumentOffset == Context(callOffset) || frame.argumentOffset == Context(applyOffset)) 297 | skip = 2; 298 | else if (frame.textIndex > Context(noIndex) && frame.ops->text.bytes == Text(nativeCode).bytes) 299 | skip = 1; 300 | 301 | frame = *frame.parent; 302 | } 303 | 304 | if (skip <= 0 && frame.ops->text.bytes != Text(nativeCode).bytes) 305 | { 306 | Context.rewindStatement(&frame); 307 | if (frame.ops->text.length) 308 | Ecc.printTextInput(frame.ecc, frame.ops->text, 0); 309 | } 310 | } 311 | } 312 | 313 | struct Object *environmentRoot (struct Context * const context) 314 | { 315 | struct Object *environment = context->strictMode? context->environment: &context->ecc->global->environment; 316 | 317 | if (context->strictMode) 318 | while (environment->prototype && environment->prototype != &context->ecc->global->environment) 319 | environment = environment->prototype; 320 | 321 | return environment; 322 | } 323 | -------------------------------------------------------------------------------- /src/pool.c: -------------------------------------------------------------------------------- 1 | // 2 | // pool.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "pool.h" 11 | 12 | // MARK: - Private 13 | 14 | static void markValue (struct Value value); 15 | static void cleanupObject(struct Object *object); 16 | 17 | static struct Pool *self = NULL; 18 | 19 | // MARK: - Static Members 20 | 21 | void markObject (struct Object *object) 22 | { 23 | uint32_t index, count; 24 | 25 | if (object->flags & Object(mark)) 26 | return; 27 | 28 | object->flags |= Object(mark); 29 | 30 | if (object->prototype) 31 | markObject(object->prototype); 32 | 33 | for (index = 0, count = object->elementCount; index < count; ++index) 34 | if (object->element[index].value.check == 1) 35 | markValue(object->element[index].value); 36 | 37 | for (index = 2, count = object->hashmapCount; index < count; ++index) 38 | if (object->hashmap[index].value.check == 1) 39 | markValue(object->hashmap[index].value); 40 | 41 | if (object->type->mark) 42 | object->type->mark(object); 43 | } 44 | 45 | static 46 | void markChars (struct Chars *chars) 47 | { 48 | if (chars->flags & Chars(mark)) 49 | return; 50 | 51 | chars->flags |= Chars(mark); 52 | } 53 | 54 | // MARK: - Methods 55 | 56 | void setup (void) 57 | { 58 | assert (!self); 59 | 60 | self = malloc(sizeof(*self)); 61 | *self = Pool.identity; 62 | } 63 | 64 | void teardown (void) 65 | { 66 | assert (self); 67 | 68 | unmarkAll(); 69 | collectUnmarked(); 70 | 71 | free(self->functionList), self->functionList = NULL; 72 | free(self->objectList), self->objectList = NULL; 73 | free(self->charsList), self->charsList = NULL; 74 | 75 | free(self), self = NULL; 76 | } 77 | 78 | void addFunction (struct Function *function) 79 | { 80 | assert(function); 81 | 82 | if (self->functionCount >= self->functionCapacity) 83 | { 84 | self->functionCapacity = self->functionCapacity? self->functionCapacity * 2: 8; 85 | self->functionList = realloc(self->functionList, self->functionCapacity * sizeof(*self->functionList)); 86 | memset(self->functionList + self->functionCount, 0, sizeof(*self->functionList) * (self->functionCapacity - self->functionCount)); 87 | } 88 | 89 | self->functionList[self->functionCount++] = function; 90 | } 91 | 92 | void addObject (struct Object *object) 93 | { 94 | assert(object); 95 | 96 | // fprintf(stderr, " > add %p %u\n", object, self->objectCount); 97 | // Object.dumpTo(object, stderr); 98 | 99 | if (self->objectCount >= self->objectCapacity) 100 | { 101 | self->objectCapacity = self->objectCapacity? self->objectCapacity * 2: 8; 102 | self->objectList = realloc(self->objectList, self->objectCapacity * sizeof(*self->objectList)); 103 | memset(self->objectList + self->objectCount, 0, sizeof(*self->objectList) * (self->objectCapacity - self->objectCount)); 104 | } 105 | 106 | self->objectList[self->objectCount++] = object; 107 | } 108 | 109 | void addChars (struct Chars *chars) 110 | { 111 | assert(chars); 112 | 113 | if (self->charsCount >= self->charsCapacity) 114 | { 115 | self->charsCapacity = self->charsCapacity? self->charsCapacity * 2: 8; 116 | self->charsList = realloc(self->charsList, self->charsCapacity * sizeof(*self->charsList)); 117 | memset(self->charsList + self->charsCount, 0, sizeof(*self->charsList) * (self->charsCapacity - self->charsCount)); 118 | } 119 | 120 | self->charsList[self->charsCount++] = chars; 121 | } 122 | 123 | void unmarkAll (void) 124 | { 125 | uint32_t index, count; 126 | 127 | for (index = 0, count = self->functionCount; index < count; ++index) 128 | { 129 | self->functionList[index]->object.flags &= ~Object(mark); 130 | self->functionList[index]->environment.flags &= ~Object(mark); 131 | } 132 | 133 | for (index = 0, count = self->objectCount; index < count; ++index) 134 | self->objectList[index]->flags &= ~Object(mark); 135 | 136 | for (index = 0, count = self->charsCount; index < count; ++index) 137 | self->charsList[index]->flags &= ~Chars(mark); 138 | } 139 | 140 | void markValue (struct Value value) 141 | { 142 | if (value.type >= Value(objectType)) 143 | markObject(value.data.object); 144 | else if (value.type == Value(charsType)) 145 | markChars(value.data.chars); 146 | } 147 | 148 | static 149 | void releaseObject(struct Object *object) 150 | { 151 | if (object->referenceCount > 0 && !--object->referenceCount) 152 | cleanupObject(object); 153 | } 154 | 155 | static 156 | struct Value releaseValue(struct Value value) 157 | { 158 | if (value.type == Value(charsType)) 159 | --value.data.chars->referenceCount; 160 | if (value.type >= Value(objectType)) 161 | releaseObject(value.data.object); 162 | 163 | return value; 164 | } 165 | 166 | static void captureObject (struct Object *object); 167 | 168 | static 169 | struct Value retainValue(struct Value value) 170 | { 171 | if (value.type == Value(charsType)) 172 | ++value.data.chars->referenceCount; 173 | if (value.type >= Value(objectType)) 174 | { 175 | ++value.data.object->referenceCount; 176 | if (!(value.data.object->flags & Object(mark))) 177 | { 178 | value.data.object->flags |= Object(mark); 179 | captureObject(value.data.object); 180 | } 181 | } 182 | return value; 183 | } 184 | 185 | static 186 | void cleanupObject(struct Object *object) 187 | { 188 | struct Value value; 189 | 190 | if (object->prototype && object->prototype->referenceCount) 191 | --object->prototype->referenceCount; 192 | 193 | if (object->elementCount) 194 | while (object->elementCount--) 195 | if ((value = object->element[object->elementCount].value).check == 1) 196 | releaseValue(value); 197 | 198 | if (object->hashmapCount) 199 | while (object->hashmapCount--) 200 | if ((value = object->hashmap[object->hashmapCount].value).check == 1) 201 | releaseValue(value); 202 | } 203 | 204 | static 205 | void captureObject (struct Object *object) 206 | { 207 | uint32_t index, count; 208 | union Object(Element) *element; 209 | union Object(Hashmap) *hashmap; 210 | 211 | if (object->prototype) 212 | { 213 | ++object->prototype->referenceCount; 214 | if (!(object->prototype->flags & Object(mark))) 215 | { 216 | object->prototype->flags |= Object(mark); 217 | captureObject(object->prototype); 218 | } 219 | } 220 | 221 | count = object->elementCount < object->elementCapacity? object->elementCount : object->elementCapacity; 222 | for (index = 0; index < count; ++index) 223 | { 224 | element = object->element + index; 225 | if (element->value.check == 1) 226 | retainValue(element->value); 227 | } 228 | 229 | count = object->hashmapCount; 230 | for (index = 2; index < count; ++index) 231 | { 232 | hashmap = object->hashmap + index; 233 | if (hashmap->value.check == 1) 234 | retainValue(hashmap->value); 235 | } 236 | 237 | if (object->type->capture) 238 | object->type->capture(object); 239 | } 240 | 241 | void collectUnmarked (void) 242 | { 243 | uint32_t index; 244 | 245 | // finalize & destroy 246 | 247 | index = self->functionCount; 248 | while (index--) 249 | if (!(self->functionList[index]->object.flags & Object(mark)) && !(self->functionList[index]->environment.flags & Object(mark))) 250 | { 251 | Function.destroy(self->functionList[index]); 252 | self->functionList[index] = self->functionList[--self->functionCount]; 253 | } 254 | 255 | index = self->objectCount; 256 | while (index--) 257 | if (!(self->objectList[index]->flags & Object(mark))) 258 | { 259 | Object.finalize(self->objectList[index]); 260 | Object.destroy(self->objectList[index]); 261 | self->objectList[index] = self->objectList[--self->objectCount]; 262 | } 263 | 264 | index = self->charsCount; 265 | while (index--) 266 | if (!(self->charsList[index]->flags & Chars(mark))) 267 | { 268 | Chars.destroy(self->charsList[index]); 269 | self->charsList[index] = self->charsList[--self->charsCount]; 270 | } 271 | } 272 | 273 | void collectUnreferencedFromIndices (uint32_t indices[3]) 274 | { 275 | uint32_t index; 276 | 277 | // prepare 278 | 279 | index = self->objectCount; 280 | while (index-- > indices[1]) 281 | if (self->objectList[index]->referenceCount <= 0) 282 | cleanupObject(self->objectList[index]); 283 | 284 | index = self->objectCount; 285 | while (index-- > indices[1]) 286 | if (self->objectList[index]->referenceCount > 0 && !(self->objectList[index]->flags & Object(mark))) 287 | { 288 | self->objectList[index]->flags |= Object(mark); 289 | captureObject(self->objectList[index]); 290 | } 291 | 292 | // destroy 293 | 294 | index = self->functionCount; 295 | while (index-- > indices[0]) 296 | if (!self->functionList[index]->object.referenceCount && !self->functionList[index]->environment.referenceCount) 297 | { 298 | Function.destroy(self->functionList[index]); 299 | self->functionList[index] = self->functionList[--self->functionCount]; 300 | } 301 | 302 | index = self->objectCount; 303 | while (index-- > indices[1]) 304 | if (self->objectList[index]->referenceCount <= 0) 305 | { 306 | Object.finalize(self->objectList[index]); 307 | Object.destroy(self->objectList[index]); 308 | self->objectList[index] = self->objectList[--self->objectCount]; 309 | } 310 | 311 | index = self->charsCount; 312 | while (index-- > indices[2]) 313 | if (self->charsList[index]->referenceCount <= 0) 314 | { 315 | Chars.destroy(self->charsList[index]); 316 | self->charsList[index] = self->charsList[--self->charsCount]; 317 | } 318 | } 319 | 320 | void unreferenceFromIndices (uint32_t indices[3]) 321 | { 322 | uint32_t index; 323 | 324 | index = self->functionCount; 325 | while (index-- > indices[0]) 326 | { 327 | --self->functionList[index]->object.referenceCount; 328 | --self->functionList[index]->environment.referenceCount; 329 | } 330 | 331 | index = self->objectCount; 332 | while (index-- > indices[1]) 333 | --self->objectList[index]->referenceCount; 334 | 335 | index = self->charsCount; 336 | while (index-- > indices[2]) 337 | --self->charsList[index]->referenceCount; 338 | } 339 | 340 | void getIndices (uint32_t indices[3]) 341 | { 342 | indices[0] = self->functionCount; 343 | indices[1] = self->objectCount; 344 | indices[2] = self->charsCount; 345 | } 346 | -------------------------------------------------------------------------------- /src/chars.c: -------------------------------------------------------------------------------- 1 | // 2 | // chars.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "chars.h" 11 | 12 | #include "ecc.h" 13 | #include "pool.h" 14 | 15 | // MARK: - Private 16 | 17 | static inline 18 | uint32_t nextPowerOfTwo(uint32_t v) 19 | { 20 | v--; 21 | v |= v >> 1; 22 | v |= v >> 2; 23 | v |= v >> 4; 24 | v |= v >> 8; 25 | v |= v >> 16; 26 | v++; 27 | return v; 28 | } 29 | 30 | static inline 31 | uint32_t sizeForLength(uint32_t length) 32 | { 33 | uint32_t size = sizeof(struct Chars) + length; 34 | 35 | if (size < 16) 36 | return 16; 37 | else 38 | return nextPowerOfTwo(size); 39 | } 40 | 41 | static 42 | struct Chars *reuseOrCreate (struct Chars(Append) *chars, uint32_t length) 43 | { 44 | struct Chars *self = NULL, *reuse = chars? chars->value: NULL; 45 | 46 | if (reuse && sizeForLength(reuse->length) >= sizeForLength(length)) 47 | return reuse; 48 | // else 49 | // chars = Pool.reusableChars(length); 50 | 51 | if (!self) 52 | { 53 | if (length < 8) 54 | return NULL; 55 | 56 | self = malloc(sizeForLength(length)); 57 | Pool.addChars(self); 58 | } 59 | 60 | if (reuse) 61 | memcpy(self, reuse, sizeof(*self) + reuse->length); 62 | else 63 | { 64 | *self = Chars.identity; 65 | self->length = chars->units; 66 | memcpy(self->bytes, chars->buffer, chars->units); 67 | } 68 | 69 | chars->value = self; 70 | return self; 71 | } 72 | 73 | // MARK: - Static Members 74 | 75 | // MARK: - Methods 76 | 77 | struct Chars * createVA (int32_t length, const char *format, va_list ap) 78 | { 79 | struct Chars *self; 80 | 81 | self = createSized(length); 82 | vsprintf(self->bytes, format, ap); 83 | 84 | return self; 85 | } 86 | 87 | struct Chars * create (const char *format, ...) 88 | { 89 | uint16_t length; 90 | va_list ap; 91 | struct Chars *self; 92 | 93 | va_start(ap, format); 94 | length = vsnprintf(NULL, 0, format, ap); 95 | va_end(ap); 96 | 97 | va_start(ap, format); 98 | self = createVA(length, format, ap); 99 | va_end(ap); 100 | 101 | return self; 102 | } 103 | 104 | struct Chars * createSized (int32_t length) 105 | { 106 | struct Chars *self = malloc(sizeForLength(length)); 107 | Pool.addChars(self); 108 | *self = Chars.identity; 109 | 110 | self->length = length; 111 | self->bytes[length] = '\0'; 112 | 113 | return self; 114 | } 115 | 116 | struct Chars * createWithBytes (int32_t length, const char *bytes) 117 | { 118 | struct Chars *self = malloc(sizeForLength(length)); 119 | Pool.addChars(self); 120 | *self = Chars.identity; 121 | 122 | self->length = length; 123 | memcpy(self->bytes, bytes, length); 124 | self->bytes[length] = '\0'; 125 | 126 | return self; 127 | } 128 | 129 | 130 | void beginAppend (struct Chars(Append) *chars) 131 | { 132 | chars->value = NULL; 133 | chars->units = 0; 134 | } 135 | 136 | void append (struct Chars(Append) *chars, const char *format, ...) 137 | { 138 | uint32_t length; 139 | va_list ap; 140 | struct Chars *self = chars->value; 141 | 142 | va_start(ap, format); 143 | length = vsnprintf(NULL, 0, format, ap); 144 | va_end(ap); 145 | 146 | self = reuseOrCreate(chars, (self? self->length: chars->units) + length); 147 | 148 | va_start(ap, format); 149 | vsprintf(self? (self->bytes + self->length): (chars->buffer + chars->units), format, ap); 150 | va_end(ap); 151 | 152 | if (self) 153 | self->length += length; 154 | else 155 | chars->units += length; 156 | } 157 | 158 | static 159 | void appendText (struct Chars(Append) * chars, struct Text text) 160 | { 161 | struct Chars *self = chars->value; 162 | struct Text(Char) lo = Text.character(text), hi = { 0 }; 163 | struct Text prev; 164 | int surrogates = 0; 165 | 166 | if (!text.length) 167 | return; 168 | 169 | if (self) 170 | prev = Text.make(self->bytes + self->length, self->length); 171 | else 172 | prev = Text.make(chars->buffer + chars->units, chars->units); 173 | 174 | if (lo.units == 3 && lo.codepoint >= 0xDC00 && lo.codepoint <= 0xDFFF) 175 | { 176 | hi = Text.prevCharacter(&prev); 177 | if (hi.units == 3 && hi.codepoint >= 0xD800 && hi.codepoint <= 0xDBFF) 178 | { 179 | surrogates = 1; 180 | Text.nextCharacter(&text); 181 | if (self) 182 | self->length = prev.length; 183 | else 184 | chars->units = prev.length; 185 | } 186 | } 187 | 188 | self = reuseOrCreate(chars, (self? self->length: chars->units) + text.length); 189 | 190 | if (surrogates) 191 | { 192 | uint32_t cp = 0x10000 + (((hi.codepoint - 0xD800) << 10) | ((lo.codepoint - 0xDC00) & 0x03FF)); 193 | 194 | if (self) 195 | self->length += writeCodepoint(self->bytes + self->length, cp); 196 | else 197 | chars->units += writeCodepoint(chars->buffer + chars->units, cp); 198 | } 199 | 200 | memcpy(self? (self->bytes + self->length): (chars->buffer + chars->units), text.bytes, text.length); 201 | if (self) 202 | self->length += text.length; 203 | else 204 | chars->units += text.length; 205 | } 206 | 207 | void appendCodepoint (struct Chars(Append) *chars, uint32_t cp) 208 | { 209 | char buffer[5] = { 0 }; 210 | struct Text text = Text.make(buffer, writeCodepoint(buffer, cp)); 211 | appendText(chars, text); 212 | } 213 | 214 | void appendValue (struct Chars(Append) *chars, struct Context * const context, struct Value value) 215 | { 216 | switch ((enum Value(Type))value.type) 217 | { 218 | case Value(keyType): 219 | case Value(textType): 220 | case Value(stringType): 221 | case Value(charsType): 222 | case Value(bufferType): 223 | appendText(chars, Value.textOf(&value)); 224 | return; 225 | 226 | case Value(nullType): 227 | appendText(chars, Text(null)); 228 | return; 229 | 230 | case Value(undefinedType): 231 | appendText(chars, Text(undefined)); 232 | return; 233 | 234 | case Value(falseType): 235 | appendText(chars, Text(false)); 236 | return; 237 | 238 | case Value(trueType): 239 | appendText(chars, Text(true)); 240 | return; 241 | 242 | case Value(booleanType): 243 | appendText(chars, value.data.boolean->truth? Text(true): Text(false)); 244 | return; 245 | 246 | case Value(integerType): 247 | appendBinary(chars, value.data.integer, 10); 248 | return; 249 | 250 | case Value(numberType): 251 | appendBinary(chars, value.data.number->value, 10); 252 | return; 253 | 254 | case Value(binaryType): 255 | appendBinary(chars, value.data.binary, 10); 256 | return; 257 | 258 | case Value(regexpType): 259 | case Value(functionType): 260 | case Value(objectType): 261 | case Value(errorType): 262 | case Value(dateType): 263 | case Value(hostType): 264 | appendValue(chars, context, Value.toString(context, value)); 265 | return; 266 | 267 | case Value(referenceType): 268 | break; 269 | } 270 | Ecc.fatal("Invalid Value(Type) : %u", value.type); 271 | } 272 | 273 | static 274 | uint32_t stripBinaryOfBytes (char *bytes, uint32_t length) 275 | { 276 | while (bytes[length - 1] == '0') 277 | bytes[--length] = '\0'; 278 | 279 | if (bytes[length - 1] == '.') 280 | bytes[--length] = '\0'; 281 | 282 | return length; 283 | } 284 | 285 | static 286 | uint32_t normalizeBinaryOfBytes (char *bytes, uint32_t length) 287 | { 288 | if (length > 5 && bytes[length - 5] == 'e' && bytes[length - 3] == '0') 289 | { 290 | bytes[length - 3] = bytes[length - 2]; 291 | bytes[length - 2] = bytes[length - 1]; 292 | bytes[length - 1] = 0; 293 | --length; 294 | } 295 | else if (length > 4 && bytes[length - 4] == 'e' && bytes[length - 2] == '0') 296 | { 297 | bytes[length - 2] = bytes[length - 1]; 298 | bytes[length - 1] = 0; 299 | --length; 300 | } 301 | return length; 302 | } 303 | 304 | void appendBinary (struct Chars(Append) *chars, double binary, int base) 305 | { 306 | if (isnan(binary)) 307 | { 308 | appendText(chars, Text(nan)); 309 | return; 310 | } 311 | else if (!isfinite(binary)) 312 | { 313 | if (binary < 0) 314 | appendText(chars, Text(negativeInfinity)); 315 | else 316 | appendText(chars, Text(infinity)); 317 | 318 | return; 319 | } 320 | 321 | if (!base || base == 10) 322 | { 323 | if (binary <= -1e+21 || binary >= 1e+21) 324 | append(chars, "%g", binary); 325 | else if ((binary < 1 && binary >= 0.000001) || (binary > -1 && binary <= -0.000001)) 326 | { 327 | append(chars, "%.10f", binary); 328 | if (chars->value) 329 | chars->value->length = stripBinaryOfBytes(chars->value->bytes, chars->value->length); 330 | else 331 | chars->units = stripBinaryOfBytes(chars->buffer, chars->units); 332 | 333 | return; 334 | } 335 | else 336 | { 337 | double dblDig10 = pow(10, DBL_DIG); 338 | int precision = binary >= -dblDig10 && binary <= dblDig10? DBL_DIG: 21; 339 | 340 | append(chars, "%.*g", precision, binary); 341 | } 342 | 343 | normalizeBinary(chars); 344 | return; 345 | } 346 | else 347 | { 348 | int sign = binary < 0; 349 | unsigned long integer = sign? -binary: binary; 350 | 351 | if (base == 8 || base == 16) 352 | { 353 | const char *format = sign? (base == 8? "-%lo": "-%lx"): (base == 8? "%lo": "%lx"); 354 | 355 | append(chars, format, integer); 356 | } 357 | else 358 | { 359 | static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 360 | char buffer[1 + sizeof(integer) * CHAR_BIT]; 361 | char *p = buffer + sizeof(buffer) - 1; 362 | uint16_t count; 363 | 364 | while (integer) { 365 | *(--p) = digits[integer % base]; 366 | integer /= base; 367 | } 368 | if (sign) 369 | *(--p) = '-'; 370 | 371 | count = buffer + sizeof(buffer) - 1 - p; 372 | append(chars, "%.*s", count, p); 373 | } 374 | } 375 | } 376 | 377 | void normalizeBinary (struct Chars(Append) *chars) 378 | { 379 | if (chars->value) 380 | chars->value->length = normalizeBinaryOfBytes(chars->value->bytes, chars->value->length); 381 | else 382 | chars->units = normalizeBinaryOfBytes(chars->buffer, chars->units); 383 | } 384 | 385 | struct Value endAppend (struct Chars(Append) *chars) 386 | { 387 | struct Chars *self = chars->value; 388 | 389 | if (chars->value) 390 | { 391 | self->bytes[self->length] = '\0'; 392 | return Value.chars(self); 393 | } 394 | else 395 | return Value.buffer(chars->buffer, chars->units); 396 | } 397 | 398 | void destroy (struct Chars *self) 399 | { 400 | assert(self); 401 | 402 | free(self), self = NULL; 403 | } 404 | 405 | uint8_t codepointLength (uint32_t cp) 406 | { 407 | if (cp < 0x80) 408 | return 1; 409 | else if (cp < 0x800) 410 | return 2; 411 | else if (cp < 0x10000) 412 | return 3; 413 | else 414 | return 4; 415 | 416 | return 0; 417 | } 418 | 419 | uint8_t writeCodepoint (char *bytes, uint32_t cp) 420 | { 421 | if (cp < 0x80) 422 | { 423 | bytes[0] = cp; 424 | return 1; 425 | } 426 | else if (cp < 0x800) 427 | { 428 | bytes[0] = 0xC0 | (cp >> 6); 429 | bytes[1] = 0x80 | (cp & 0x3F); 430 | return 2; 431 | } 432 | else if (cp < 0x10000) 433 | { 434 | bytes[0] = 0xE0 | (cp >> 12); 435 | bytes[1] = 0x80 | (cp >> 6 & 0x3F); 436 | bytes[2] = 0x80 | (cp & 0x3F); 437 | return 3; 438 | } 439 | else 440 | { 441 | bytes[0] = 0xF0 | (cp >> 18); 442 | bytes[1] = 0x80 | (cp >> 12 & 0x3F); 443 | bytes[2] = 0x80 | (cp >> 6 & 0x3F); 444 | bytes[3] = 0x80 | (cp & 0x3F); 445 | return 4; 446 | } 447 | 448 | return 0; 449 | } 450 | -------------------------------------------------------------------------------- /src/builtin/global.c: -------------------------------------------------------------------------------- 1 | // 2 | // global.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "global.h" 11 | 12 | #include "../ecc.h" 13 | #include "../lexer.h" 14 | 15 | // MARK: - Private 16 | 17 | const struct Object(Type) Global(type) = { 18 | .text = &Text(globalType), 19 | }; 20 | 21 | // MARK: - Static Members 22 | 23 | static 24 | struct Value eval (struct Context * const context) 25 | { 26 | struct Value value; 27 | struct Input *input; 28 | struct Context subContext = { 29 | .parent = context, 30 | .this = Value.object(&context->ecc->global->environment), 31 | .ecc = context->ecc, 32 | .depth = context->depth + 1, 33 | .environment = Context.environmentRoot(context->parent), 34 | }; 35 | 36 | value = Context.argument(context, 0); 37 | if (!Value.isString(value) || !Value.isPrimitive(value)) 38 | return value; 39 | 40 | input = Input.createFromBytes(Value.stringBytes(&value), Value.stringLength(&value), "(eval)"); 41 | 42 | Context.setTextIndex(context, Context(noIndex)); 43 | Ecc.evalInputWithContext(context->ecc, input, &subContext); 44 | 45 | return context->ecc->result; 46 | } 47 | 48 | static 49 | struct Value parseInt (struct Context * const context) 50 | { 51 | struct Value value; 52 | struct Text text; 53 | int32_t base; 54 | 55 | value = Value.toString(context, Context.argument(context, 0)); 56 | base = Value.toInteger(context, Context.argument(context, 1)).data.integer; 57 | text = Value.textOf(&value); 58 | 59 | if (!base) 60 | { 61 | // prevent octal auto-detection 62 | 63 | if (text.length > 2 && text.bytes[0] == '-') 64 | { 65 | if (text.bytes[1] == '0' && tolower(text.bytes[2]) != 'x') 66 | base = 10; 67 | } 68 | else if (text.length > 1 && text.bytes[0] == '0' && tolower(text.bytes[1]) != 'x') 69 | base = 10; 70 | } 71 | 72 | return Lexer.scanInteger(text, base, Lexer(scanLazy) | (context->ecc->sloppyMode? Lexer(scanSloppy): 0)); 73 | } 74 | 75 | static 76 | struct Value parseFloat (struct Context * const context) 77 | { 78 | struct Value value; 79 | struct Text text; 80 | 81 | value = Value.toString(context, Context.argument(context, 0)); 82 | text = Value.textOf(&value); 83 | return Lexer.scanBinary(text, Lexer(scanLazy) | (context->ecc->sloppyMode? Lexer(scanSloppy): 0)); 84 | } 85 | 86 | static 87 | struct Value isFinite (struct Context * const context) 88 | { 89 | struct Value value; 90 | 91 | value = Value.toBinary(context, Context.argument(context, 0)); 92 | return Value.truth(!isnan(value.data.binary) && !isinf(value.data.binary)); 93 | } 94 | 95 | static 96 | struct Value isNaN (struct Context * const context) 97 | { 98 | struct Value value; 99 | 100 | value = Value.toBinary(context, Context.argument(context, 0)); 101 | return Value.truth(isnan(value.data.binary)); 102 | } 103 | 104 | static 105 | struct Value decodeExcept (struct Context * const context, const char *exclude) 106 | { 107 | char buffer[5], *b; 108 | struct Value value; 109 | const char *bytes; 110 | uint16_t index = 0, count; 111 | struct Chars(Append) chars; 112 | uint8_t byte; 113 | 114 | value = Value.toString(context, Context.argument(context, 0)); 115 | bytes = Value.stringBytes(&value); 116 | count = Value.stringLength(&value); 117 | 118 | Chars.beginAppend(&chars); 119 | 120 | while (index < count) 121 | { 122 | byte = bytes[index++]; 123 | 124 | if (byte != '%') 125 | Chars.append(&chars, "%c", byte); 126 | else if (index + 2 > count || !isxdigit(bytes[index]) || !isxdigit(bytes[index + 1])) 127 | goto error; 128 | else 129 | { 130 | byte = Lexer.uint8Hex(bytes[index], bytes[index + 1]); 131 | index += 2; 132 | 133 | if (byte >= 0x80) 134 | { 135 | struct Text(Char) c; 136 | int continuation = (byte & 0xf8) == 0xf0? 3: (byte & 0xf0) == 0xe0? 2: (byte & 0xe0) == 0xc0? 1: 0; 137 | 138 | if (!continuation || index + continuation * 3 > count) 139 | goto error; 140 | 141 | b = buffer; 142 | (*b++) = byte; 143 | while (continuation--) 144 | { 145 | if (bytes[index++] != '%' || !isxdigit(bytes[index]) || !isxdigit(bytes[index + 1])) 146 | goto error; 147 | 148 | byte = Lexer.uint8Hex(bytes[index], bytes[index + 1]); 149 | index += 2; 150 | 151 | if ((byte & 0xc0) != 0x80) 152 | goto error; 153 | 154 | (*b++) = byte; 155 | } 156 | *b = '\0'; 157 | 158 | c = Text.character(Text.make(buffer, (int32_t)(b - buffer))); 159 | Chars.appendCodepoint(&chars, c.codepoint); 160 | } 161 | else if (byte && exclude && strchr(exclude, byte)) 162 | Chars.append(&chars, "%%%c%c", bytes[index - 2], bytes[index - 1]); 163 | else 164 | Chars.append(&chars, "%c", byte); 165 | } 166 | } 167 | 168 | return Chars.endAppend(&chars); 169 | 170 | error: 171 | Context.uriError(context, Chars.create("malformed URI")); 172 | } 173 | 174 | static 175 | struct Value decodeURI (struct Context * const context) 176 | { 177 | return decodeExcept(context, ";/?:@&=+$,#"); 178 | } 179 | 180 | static 181 | struct Value decodeURIComponent (struct Context * const context) 182 | { 183 | return decodeExcept(context, NULL); 184 | } 185 | 186 | static 187 | struct Value encodeExpect (struct Context * const context, const char *exclude) 188 | { 189 | const char hex[] = "0123456789ABCDEF"; 190 | struct Value value; 191 | const char *bytes; 192 | uint16_t offset = 0, unit, length; 193 | struct Chars *chars; 194 | struct Text text; 195 | struct Text(Char) c; 196 | int needPair = 0; 197 | 198 | value = Value.toString(context, Context.argument(context, 0)); 199 | bytes = Value.stringBytes(&value); 200 | length = Value.stringLength(&value); 201 | text = Text.make(bytes, length); 202 | 203 | chars = Chars.createSized(length * 3); 204 | 205 | while (text.length) 206 | { 207 | c = Text.character(text); 208 | 209 | if (c.codepoint < 0x80 && c.codepoint && strchr(exclude, c.codepoint)) 210 | chars->bytes[offset++] = c.codepoint; 211 | else 212 | { 213 | if (c.codepoint >= 0xDC00 && c.codepoint <= 0xDFFF) 214 | { 215 | if (!needPair) 216 | goto error; 217 | } 218 | else if (needPair) 219 | goto error; 220 | else if (c.codepoint >= 0xD800 && c.codepoint <= 0xDBFF) 221 | needPair = 1; 222 | 223 | for (unit = 0; unit < c.units; ++unit) 224 | { 225 | chars->bytes[offset++] = '%'; 226 | chars->bytes[offset++] = hex[(uint8_t)text.bytes[unit] >> 4]; 227 | chars->bytes[offset++] = hex[(uint8_t)text.bytes[unit] & 0xf]; 228 | } 229 | } 230 | 231 | Text.advance(&text, c.units); 232 | } 233 | 234 | if (needPair) 235 | goto error; 236 | 237 | chars->length = offset; 238 | return Value.chars(chars); 239 | 240 | error: 241 | Context.uriError(context, Chars.create("malformed URI")); 242 | } 243 | 244 | static 245 | struct Value encodeURI (struct Context * const context) 246 | { 247 | return encodeExpect(context, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()" ";/?:@&=+$,#"); 248 | } 249 | 250 | static 251 | struct Value encodeURIComponent (struct Context * const context) 252 | { 253 | return encodeExpect(context, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()"); 254 | } 255 | 256 | static 257 | struct Value escape (struct Context * const context) 258 | { 259 | const char *exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 @*_+-./"; 260 | struct Value value; 261 | struct Chars(Append) chars; 262 | struct Text text; 263 | struct Text(Char) c; 264 | 265 | value = Value.toString(context, Context.argument(context, 0)); 266 | text = Value.textOf(&value); 267 | 268 | Chars.beginAppend(&chars); 269 | 270 | while (text.length) 271 | { 272 | c = Text.nextCharacter(&text); 273 | 274 | if (c.codepoint < 0x80 && c.codepoint && strchr(exclude, c.codepoint)) 275 | Chars.append(&chars, "%c", c.codepoint); 276 | else 277 | { 278 | if (c.codepoint <= 0xff) 279 | Chars.append(&chars, "%%%02X", c.codepoint); 280 | else if (c.codepoint < 0x010000) 281 | Chars.append(&chars, "%%u%04X", c.codepoint); 282 | else 283 | { 284 | c.codepoint -= 0x010000; 285 | Chars.append(&chars, "%%u%04X", ((c.codepoint >> 10) & 0x3ff) + 0xd800); 286 | Chars.append(&chars, "%%u%04X", ((c.codepoint >> 0) & 0x3ff) + 0xdc00); 287 | } 288 | } 289 | } 290 | 291 | return Chars.endAppend(&chars); 292 | } 293 | 294 | static 295 | struct Value unescape (struct Context * const context) 296 | { 297 | struct Value value; 298 | struct Chars(Append) chars; 299 | struct Text text; 300 | struct Text(Char) c; 301 | 302 | value = Value.toString(context, Context.argument(context, 0)); 303 | text = Value.textOf(&value); 304 | 305 | Chars.beginAppend(&chars); 306 | 307 | while (text.length) 308 | { 309 | c = Text.nextCharacter(&text); 310 | 311 | if (c.codepoint == '%') 312 | { 313 | switch (Text.character(text).codepoint) 314 | { 315 | case '%': 316 | Chars.append(&chars, "%%"); 317 | break; 318 | 319 | case 'u': 320 | { 321 | uint32_t cp = Lexer.uint16Hex(text.bytes[1], text.bytes[2], text.bytes[3], text.bytes[4]); 322 | 323 | Chars.appendCodepoint(&chars, cp); 324 | Text.advance(&text, 5); 325 | break; 326 | } 327 | 328 | default: 329 | { 330 | uint32_t cp = Lexer.uint8Hex(text.bytes[0], text.bytes[1]); 331 | 332 | Chars.appendCodepoint(&chars, cp); 333 | Text.advance(&text, 2); 334 | break; 335 | } 336 | } 337 | } 338 | else 339 | Chars.append(&chars, "%c", c.codepoint); 340 | } 341 | 342 | return Chars.endAppend(&chars); 343 | } 344 | 345 | // MARK: - Methods 346 | 347 | void setup (void) 348 | { 349 | Function(prototype) = Object(prototype) = Object.create(NULL); 350 | 351 | Function.setup(); 352 | Object.setup(); 353 | String.setup(); 354 | Error.setup(); 355 | Array.setup(); 356 | Date.setup(); 357 | Math.setup(); 358 | Number.setup(); 359 | Boolean.setup(); 360 | RegExp.setup(); 361 | JSON.setup(); 362 | Arguments.setup(); 363 | } 364 | 365 | void teardown (void) 366 | { 367 | Function.teardown(); 368 | Object.teardown(); 369 | String.teardown(); 370 | Error.teardown(); 371 | Array.teardown(); 372 | Date.teardown(); 373 | Math.teardown(); 374 | Number.teardown(); 375 | Boolean.teardown(); 376 | RegExp.teardown(); 377 | JSON.teardown(); 378 | Arguments.teardown(); 379 | } 380 | 381 | struct Function * create (void) 382 | { 383 | const enum Value(Flags) r = Value(readonly); 384 | const enum Value(Flags) h = Value(hidden); 385 | const enum Value(Flags) s = Value(sealed); 386 | 387 | struct Function * self = Function.create(Object(prototype)); 388 | self->environment.type = &Global(type); 389 | 390 | Function.addValue(self, "NaN", Value.binary(NAN), r|h|s); 391 | Function.addValue(self, "Infinity", Value.binary(INFINITY), r|h|s); 392 | Function.addValue(self, "undefined", Value(undefined), r|h|s); 393 | 394 | Function.addFunction(self, "eval", eval, 1, h); 395 | Function.addFunction(self, "escape", escape, 1, h); 396 | Function.addFunction(self, "unescape", unescape, 1, h); 397 | Function.addFunction(self, "parseInt", parseInt, 2, h); 398 | Function.addFunction(self, "parseFloat", parseFloat, 1, h); 399 | Function.addFunction(self, "isNaN", isNaN, 1, h); 400 | Function.addFunction(self, "isFinite", isFinite, 1, h); 401 | Function.addFunction(self, "decodeURI", decodeURI, 1, h); 402 | Function.addFunction(self, "decodeURIComponent", decodeURIComponent, 1, h); 403 | Function.addFunction(self, "encodeURI", encodeURI, 1, h); 404 | Function.addFunction(self, "encodeURIComponent", encodeURIComponent, 1, h); 405 | Function.addValue(self, "Object", Value.function(Object(constructor)), h); 406 | Function.addValue(self, "Function", Value.function(Function(constructor)), h); 407 | Function.addValue(self, "Array", Value.function(Array(constructor)), h); 408 | Function.addValue(self, "String", Value.function(String(constructor)), h); 409 | Function.addValue(self, "Boolean", Value.function(Boolean(constructor)), h); 410 | Function.addValue(self, "Number", Value.function(Number(constructor)), h); 411 | Function.addValue(self, "Date", Value.function(Date(constructor)), h); 412 | Function.addValue(self, "RegExp", Value.function(RegExp(constructor)), h); 413 | Function.addValue(self, "Error", Value.function(Error(constructor)), h); 414 | Function.addValue(self, "RangeError", Value.function(Error(rangeConstructor)), h); 415 | Function.addValue(self, "ReferenceError", Value.function(Error(referenceConstructor)), h); 416 | Function.addValue(self, "SyntaxError", Value.function(Error(syntaxConstructor)), h); 417 | Function.addValue(self, "TypeError", Value.function(Error(typeConstructor)), h); 418 | Function.addValue(self, "URIError", Value.function(Error(uriConstructor)), h); 419 | Function.addValue(self, "EvalError", Value.function(Error(evalConstructor)), h); 420 | Function.addValue(self, "Math", Value.object(Math(object)), h); 421 | Function.addValue(self, "JSON", Value.object(JSON(object)), h); 422 | 423 | return self; 424 | } 425 | -------------------------------------------------------------------------------- /src/builtin/function.c: -------------------------------------------------------------------------------- 1 | // 2 | // function.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "function.h" 11 | 12 | #include "../ecc.h" 13 | #include "../oplist.h" 14 | #include "../pool.h" 15 | 16 | // MARK: - Private 17 | 18 | static va_list empty_ap; 19 | 20 | static void mark (struct Object *object); 21 | static void capture (struct Object *object); 22 | 23 | struct Object * Function(prototype) = NULL; 24 | struct Function * Function(constructor) = NULL; 25 | 26 | const struct Object(Type) Function(type) = { 27 | .text = &Text(functionType), 28 | .mark = mark, 29 | .capture = capture, 30 | /* XXX: don't finalize */ 31 | }; 32 | 33 | static 34 | void capture (struct Object *object) 35 | { 36 | struct Function *self = (struct Function *)object; 37 | 38 | if (self->refObject) 39 | ++self->refObject->referenceCount; 40 | 41 | if (self->pair) 42 | ++self->pair->object.referenceCount; 43 | } 44 | 45 | static 46 | void mark (struct Object *object) 47 | { 48 | struct Function *self = (struct Function *)object; 49 | 50 | Pool.markObject(&self->environment); 51 | 52 | if (self->refObject) 53 | Pool.markObject(self->refObject); 54 | 55 | if (self->pair) 56 | Pool.markObject(&self->pair->object); 57 | } 58 | 59 | // MARK: - Static Members 60 | 61 | static 62 | struct Value toChars (struct Context * const context, struct Value value) 63 | { 64 | struct Function *self; 65 | struct Chars(Append) chars; 66 | 67 | assert(value.type == Value(functionType)); 68 | assert(value.data.function); 69 | 70 | self = value.data.function; 71 | Chars.beginAppend(&chars); 72 | 73 | Chars.append(&chars, "function %s", self->name? self->name: "anonymous"); 74 | 75 | if (self->text.bytes == Text(nativeCode).bytes) 76 | Chars.append(&chars, "() [native code]"); 77 | else 78 | Chars.append(&chars, "%.*s", self->text.length, self->text.bytes); 79 | 80 | return Chars.endAppend(&chars); 81 | } 82 | 83 | static 84 | struct Value toString (struct Context * const context) 85 | { 86 | Context.assertThisType(context, Value(functionType)); 87 | 88 | return toChars(context, context->this); 89 | } 90 | 91 | static 92 | struct Value apply (struct Context * const context) 93 | { 94 | struct Value this, arguments; 95 | 96 | Context.assertThisType(context, Value(functionType)); 97 | 98 | context->strictMode = context->parent->strictMode; 99 | 100 | this = Context.argument(context, 0); 101 | if (this.type != Value(undefinedType) && this.type != Value(nullType)) 102 | this = Value.toObject(context, this); 103 | 104 | arguments = Context.argument(context, 1); 105 | 106 | if (arguments.type == Value(undefinedType) || arguments.type == Value(nullType)) 107 | return Op.callFunctionVA(context, Context(applyOffset), context->this.data.function, this, 0, empty_ap); 108 | else 109 | { 110 | if (!Value.isObject(arguments)) 111 | Context.typeError(context, Chars.create("arguments is not an object")); 112 | 113 | return Op.callFunctionArguments(context, Context(applyOffset), context->this.data.function, this, arguments.data.object); 114 | } 115 | } 116 | 117 | static 118 | struct Value call (struct Context * const context) 119 | { 120 | struct Object arguments; 121 | 122 | Context.assertThisType(context, Value(functionType)); 123 | 124 | context->strictMode = context->parent->strictMode; 125 | 126 | arguments = *context->environment->hashmap[2].value.data.object; 127 | 128 | if (arguments.elementCount) 129 | { 130 | struct Value this = Context.argument(context, 0); 131 | if (this.type != Value(undefinedType) && this.type != Value(nullType)) 132 | this = Value.toObject(context, this); 133 | 134 | --arguments.elementCapacity; 135 | --arguments.elementCount; 136 | ++arguments.element; 137 | if (!arguments.elementCount) 138 | { 139 | arguments.element = NULL; 140 | arguments.elementCapacity = 0; 141 | } 142 | 143 | return Op.callFunctionArguments(context, Context(callOffset), context->this.data.function, this, &arguments); 144 | } 145 | else 146 | return Op.callFunctionVA(context, Context(callOffset), context->this.data.function, Value(undefined), 0, empty_ap); 147 | } 148 | 149 | static 150 | struct Value bindCall (struct Context * const context) 151 | { 152 | struct Function *function; 153 | struct Object *arguments; 154 | uint16_t count, length; 155 | 156 | Context.assertThisType(context, Value(functionType)); 157 | 158 | context->strictMode = context->parent->strictMode; 159 | 160 | function = context->this.data.function; 161 | 162 | count = Context.argumentCount(context); 163 | length = (function->environment.elementCount - 1) + count; 164 | arguments = Array.createSized(length); 165 | 166 | memcpy(arguments->element, function->environment.element + 1, sizeof(*arguments->element) * (function->environment.elementCount - 1)); 167 | memcpy(arguments->element + (function->environment.elementCount - 1), context->environment->hashmap[2].value.data.object->element, sizeof(*arguments->element) * (context->environment->hashmap[2].value.data.object->elementCount)); 168 | 169 | return Op.callFunctionArguments(context, 0, context->this.data.function->pair, function->environment.element[0].value, arguments); 170 | } 171 | 172 | static 173 | struct Value bind (struct Context * const context) 174 | { 175 | struct Function *function; 176 | uint16_t index, count; 177 | int parameterCount = 0; 178 | 179 | Context.assertThisType(context, Value(functionType)); 180 | 181 | count = Context.argumentCount(context); 182 | parameterCount = context->this.data.function->parameterCount - (count > 1? count - 1: 0); 183 | function = createWithNative(bindCall, parameterCount > 0? parameterCount: 0); 184 | 185 | Object.resizeElement(&function->environment, count? count: 1); 186 | if (count) 187 | for (index = 0; index < count; ++index) 188 | function->environment.element[index].value = Context.argument(context, index); 189 | else 190 | function->environment.element[0].value = Value(undefined); 191 | 192 | function->pair = context->this.data.function; 193 | function->boundThis = Value.function(function); 194 | function->flags |= Function(needArguments) | Function(useBoundThis); 195 | 196 | return Value.function(function); 197 | } 198 | 199 | static 200 | struct Value prototypeConstructor (struct Context * const context) 201 | { 202 | return Value(undefined); 203 | } 204 | 205 | static 206 | struct Value constructor (struct Context * const context) 207 | { 208 | int argumentCount; 209 | 210 | argumentCount = Context.argumentCount(context); 211 | 212 | { 213 | int_fast32_t index; 214 | struct Value value; 215 | struct Chars(Append) chars; 216 | struct Input *input; 217 | struct Context subContext = { 218 | .parent = context, 219 | .this = Value.object(&context->ecc->global->environment), 220 | .ecc = context->ecc, 221 | .depth = context->depth + 1, 222 | .environment = Context.environmentRoot(context->parent), 223 | }; 224 | 225 | Chars.beginAppend(&chars); 226 | Chars.append(&chars, "(function ("); 227 | if (argumentCount) 228 | for (index = 0; index < argumentCount; ++index) 229 | { 230 | if (index == argumentCount - 1) 231 | Chars.append(&chars, "){"); 232 | 233 | value = Value.toString(context, Context.argument(context, index)); 234 | Chars.append(&chars, "%.*s", Value.stringLength(&value), Value.stringBytes(&value)); 235 | 236 | if (index < argumentCount - 2) 237 | Chars.append(&chars, ","); 238 | } 239 | else 240 | Chars.append(&chars, "){"); 241 | 242 | Chars.append(&chars, "})"); 243 | 244 | value = Chars.endAppend(&chars); 245 | input = Input.createFromBytes(Value.stringBytes(&value), Value.stringLength(&value), "(Function)"); 246 | Context.setTextIndex(context, Context(noIndex)); 247 | Ecc.evalInputWithContext(context->ecc, input, &subContext); 248 | } 249 | 250 | return context->ecc->result; 251 | } 252 | 253 | // MARK: - Methods 254 | 255 | void setup () 256 | { 257 | const enum Value(Flags) h = Value(hidden); 258 | 259 | Function.setupBuiltinObject( 260 | &Function(constructor), constructor, -1, 261 | &Function(prototype), Value.function(createWithNative(prototypeConstructor, 0)), 262 | &Function(type)); 263 | 264 | Function(constructor)->object.prototype = Function(prototype); 265 | 266 | Function.addToObject(Function(prototype), "toString", toString, 0, h); 267 | Function.addToObject(Function(prototype), "apply", apply, 2, h); 268 | Function.addToObject(Function(prototype), "call", call, -1, h); 269 | Function.addToObject(Function(prototype), "bind", bind, -1, h); 270 | } 271 | 272 | void teardown (void) 273 | { 274 | Function(prototype) = NULL; 275 | Function(constructor) = NULL; 276 | } 277 | 278 | struct Function * create (struct Object *environment) 279 | { 280 | return createSized(environment, 8); 281 | } 282 | 283 | struct Function * createSized (struct Object *environment, uint32_t size) 284 | { 285 | struct Function *self = malloc(sizeof(*self)); 286 | Pool.addFunction(self); 287 | 288 | *self = Function.identity; 289 | 290 | Object.initialize(&self->object, Function(prototype)); 291 | Object.initializeSized(&self->environment, environment, size); 292 | 293 | return self; 294 | } 295 | 296 | struct Function * createWithNative (const Native(Function) native, int parameterCount) 297 | { 298 | struct Function *self = NULL; 299 | 300 | if (parameterCount < 0) 301 | { 302 | self = createSized(NULL, 3); 303 | self->flags |= Function(needArguments); 304 | } 305 | else 306 | { 307 | self = createSized(NULL, 3 + parameterCount); 308 | self->parameterCount = parameterCount; 309 | } 310 | 311 | self->environment.hashmapCount = self->environment.hashmapCapacity; 312 | self->oplist = OpList.create(native, Value(undefined), Text(nativeCode)); 313 | self->text = Text(nativeCode); 314 | 315 | Object.addMember(&self->object, Key(length), Value.integer(abs(parameterCount)), Value(readonly) | Value(hidden) | Value(sealed)); 316 | 317 | return self; 318 | } 319 | 320 | struct Function * copy (struct Function *original) 321 | { 322 | struct Function *self = malloc(sizeof(*self)); 323 | size_t byteSize; 324 | 325 | assert(original); 326 | Pool.addObject(&self->object); 327 | 328 | *self = *original; 329 | 330 | byteSize = sizeof(*self->object.hashmap) * self->object.hashmapCapacity; 331 | self->object.hashmap = malloc(byteSize); 332 | memcpy(self->object.hashmap, original->object.hashmap, byteSize); 333 | 334 | return self; 335 | } 336 | 337 | void destroy (struct Function *self) 338 | { 339 | assert(self); 340 | 341 | Object.finalize(&self->object); 342 | Object.finalize(&self->environment); 343 | 344 | if (self->oplist) 345 | OpList.destroy(self->oplist), self->oplist = NULL; 346 | 347 | free(self), self = NULL; 348 | } 349 | 350 | void addMember(struct Function *self, const char *name, struct Value value, enum Value(Flags) flags) 351 | { 352 | assert(self); 353 | 354 | if (value.type == Value(functionType)) 355 | value.data.function->name = name; 356 | 357 | Object.addMember(&self->object, Key.makeWithCString(name), value, flags); 358 | } 359 | 360 | struct Function * addMethod(struct Function *self, const char *name, const Native(Function) native, int parameterCount, enum Value(Flags) flags) 361 | { 362 | assert(self); 363 | 364 | return addToObject(&self->object, name, native, parameterCount, flags); 365 | } 366 | 367 | void addValue(struct Function *self, const char *name, struct Value value, enum Value(Flags) flags) 368 | { 369 | assert(self); 370 | 371 | if (value.type == Value(functionType)) 372 | value.data.function->name = name; 373 | 374 | Object.addMember(&self->environment, Key.makeWithCString(name), value, flags); 375 | } 376 | 377 | struct Function * addFunction(struct Function *self, const char *name, const Native(Function) native, int parameterCount, enum Value(Flags) flags) 378 | { 379 | assert(self); 380 | 381 | return addToObject(&self->environment, name, native, parameterCount, flags); 382 | } 383 | 384 | struct Function * addToObject(struct Object *object, const char *name, const Native(Function) native, int parameterCount, enum Value(Flags) flags) 385 | { 386 | struct Function *function; 387 | 388 | assert(object); 389 | 390 | function = createWithNative(native, parameterCount); 391 | function->name = name; 392 | 393 | Object.addMember(object, Key.makeWithCString(name), Value.function(function), flags); 394 | 395 | return function; 396 | } 397 | 398 | void linkPrototype (struct Function *self, struct Value prototype, enum Value(Flags) flags) 399 | { 400 | assert(self); 401 | 402 | Object.addMember(prototype.data.object, Key(constructor), Value.function(self), Value(hidden)); 403 | Object.addMember(&self->object, Key(prototype), prototype, flags); 404 | } 405 | 406 | void setupBuiltinObject (struct Function **constructor, const Native(Function) native, int parameterCount, struct Object **prototype, struct Value prototypeValue, const struct Object(Type) *type) 407 | { 408 | struct Function *function = createWithNative(native, parameterCount); 409 | 410 | if (prototype) 411 | { 412 | struct Object *object = prototypeValue.data.object; 413 | object->type = type; 414 | 415 | if (!object->prototype) 416 | object->prototype = Object(prototype); 417 | 418 | *prototype = object; 419 | } 420 | 421 | linkPrototype(function, prototypeValue, Value(readonly) | Value(hidden) | Value(sealed)); 422 | *constructor = function; 423 | } 424 | 425 | struct Value accessor (const Native(Function) getter, const Native(Function) setter) 426 | { 427 | struct Value value; 428 | struct Function *getterFunction = NULL, *setterFunction = NULL; 429 | if (setter) 430 | setterFunction = Function.createWithNative(setter, 1); 431 | 432 | if (getter) 433 | { 434 | getterFunction = Function.createWithNative(getter, 0); 435 | getterFunction->pair = setterFunction; 436 | value = Value.function(getterFunction); 437 | value.flags |= Value(getter); 438 | } 439 | else if (setter) 440 | { 441 | value = Value.function(setterFunction); 442 | value.flags |= Value(setter); 443 | } 444 | else 445 | value = Value(undefined); 446 | 447 | return value; 448 | } 449 | -------------------------------------------------------------------------------- /src/oplist.c: -------------------------------------------------------------------------------- 1 | // 2 | // oplist.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "oplist.h" 11 | 12 | // MARK: - Private 13 | 14 | // MARK: - Static Members 15 | 16 | // MARK: - Methods 17 | 18 | struct OpList * create (const Native(Function) native, struct Value value, struct Text text) 19 | { 20 | struct OpList *self = malloc(sizeof(*self)); 21 | self->ops = malloc(sizeof(*self->ops) * 1); 22 | self->ops[0] = Op.make(native, value, text); 23 | self->count = 1; 24 | return self; 25 | } 26 | 27 | void destroy (struct OpList * self) 28 | { 29 | assert(self); 30 | 31 | free(self->ops), self->ops = NULL; 32 | free(self), self = NULL; 33 | } 34 | 35 | struct OpList * join (struct OpList *self, struct OpList *with) 36 | { 37 | if (!self) 38 | return with; 39 | else if (!with) 40 | return self; 41 | 42 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + with->count)); 43 | memcpy(self->ops + self->count, with->ops, sizeof(*self->ops) * with->count); 44 | self->count += with->count; 45 | 46 | destroy(with), with = NULL; 47 | 48 | return self; 49 | } 50 | 51 | struct OpList * join3 (struct OpList *self, struct OpList *a, struct OpList *b) 52 | { 53 | if (!self) 54 | return join(a, b); 55 | else if (!a) 56 | return join(self, b); 57 | else if (!b) 58 | return join(self, a); 59 | 60 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + a->count + b->count)); 61 | memcpy(self->ops + self->count, a->ops, sizeof(*self->ops) * a->count); 62 | memcpy(self->ops + self->count + a->count, b->ops, sizeof(*self->ops) * b->count); 63 | self->count += a->count + b->count; 64 | 65 | destroy(a), a = NULL; 66 | destroy(b), b = NULL; 67 | 68 | return self; 69 | } 70 | 71 | struct OpList * joinDiscarded (struct OpList *self, uint16_t n, struct OpList *with) 72 | { 73 | while (n > 16) 74 | { 75 | self = OpList.append(self, Op.make(Op.discardN, Value.integer(16), Text(empty))); 76 | n -= 16; 77 | } 78 | 79 | if (n == 1) 80 | self = OpList.append(self, Op.make(Op.discard, Value(undefined), Text(empty))); 81 | else 82 | self = OpList.append(self, Op.make(Op.discardN, Value.integer(n), Text(empty))); 83 | 84 | return join(self, with); 85 | } 86 | 87 | struct OpList * unshift (struct Op op, struct OpList *self) 88 | { 89 | if (!self) 90 | return create(op.native, op.value, op.text); 91 | 92 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + 1)); 93 | memmove(self->ops + 1, self->ops, sizeof(*self->ops) * self->count++); 94 | self->ops[0] = op; 95 | return self; 96 | } 97 | 98 | struct OpList * unshiftJoin (struct Op op, struct OpList *self, struct OpList *with) 99 | { 100 | if (!self) 101 | return unshift(op, with); 102 | else if (!with) 103 | return unshift(op, self); 104 | 105 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + with->count + 1)); 106 | memmove(self->ops + 1, self->ops, sizeof(*self->ops) * self->count); 107 | memcpy(self->ops + self->count + 1, with->ops, sizeof(*self->ops) * with->count); 108 | self->ops[0] = op; 109 | self->count += with->count + 1; 110 | 111 | destroy(with), with = NULL; 112 | 113 | return self; 114 | } 115 | 116 | struct OpList * unshiftJoin3 (struct Op op, struct OpList *self, struct OpList *a, struct OpList *b) 117 | { 118 | if (!self) 119 | return unshiftJoin(op, a, b); 120 | else if (!a) 121 | return unshiftJoin(op, self, b); 122 | else if (!b) 123 | return unshiftJoin(op, self, a); 124 | 125 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + a->count + b->count + 1)); 126 | memmove(self->ops + 1, self->ops, sizeof(*self->ops) * self->count); 127 | memcpy(self->ops + self->count + 1, a->ops, sizeof(*self->ops) * a->count); 128 | memcpy(self->ops + self->count + a->count + 1, b->ops, sizeof(*self->ops) * b->count); 129 | self->ops[0] = op; 130 | self->count += a->count + b->count + 1; 131 | 132 | destroy(a), a = NULL; 133 | destroy(b), b = NULL; 134 | 135 | return self; 136 | } 137 | 138 | struct OpList * shift (struct OpList *self) 139 | { 140 | memmove(self->ops, self->ops + 1, sizeof(*self->ops) * --self->count); 141 | return self; 142 | } 143 | 144 | struct OpList * append (struct OpList *self, struct Op op) 145 | { 146 | if (!self) 147 | return create(op.native, op.value, op.text); 148 | 149 | self->ops = realloc(self->ops, sizeof(*self->ops) * (self->count + 1)); 150 | self->ops[self->count++] = op; 151 | return self; 152 | } 153 | 154 | struct OpList * appendNoop (struct OpList *self) 155 | { 156 | return append(self, Op.make(Op.noop, Value(undefined), Text(empty))); 157 | } 158 | 159 | struct OpList * createLoop (struct OpList * initial, struct OpList * condition, struct OpList * step, struct OpList * body, int reverseCondition) 160 | { 161 | if (condition && step && condition->count == 3 && !reverseCondition) 162 | { 163 | if (condition->ops[1].native == Op.getLocal && ( 164 | condition->ops[0].native == Op.less || 165 | condition->ops[0].native == Op.lessOrEqual )) 166 | if (step->count >= 2 && step->ops[1].value.data.key.data.integer == condition->ops[1].value.data.key.data.integer) 167 | { 168 | struct Value stepValue; 169 | if (step->count == 2 && (step->ops[0].native == Op.incrementRef || step->ops[0].native == Op.postIncrementRef)) 170 | stepValue = Value.integer(1); 171 | else if (step->count == 3 && step->ops[0].native == Op.addAssignRef && step->ops[2].native == Op.value && step->ops[2].value.type == Value(integerType) && step->ops[2].value.data.integer > 0) 172 | stepValue = step->ops[2].value; 173 | else 174 | goto normal; 175 | 176 | if (condition->ops[2].native == Op.getLocal) 177 | body = OpList.unshift(Op.make(Op.getLocalRef, condition->ops[2].value, condition->ops[2].text), body); 178 | else if (condition->ops[2].native == Op.value) 179 | body = OpList.unshift(Op.make(Op.valueConstRef, condition->ops[2].value, condition->ops[2].text), body); 180 | else 181 | goto normal; 182 | 183 | body = OpList.appendNoop(OpList.unshift(Op.make(Op.getLocalRef, condition->ops[1].value, condition->ops[1].text), body)); 184 | body = OpList.unshift(Op.make(Op.value, stepValue, condition->ops[0].text), body); 185 | body = OpList.unshift(Op.make(condition->ops[0].native == Op.less? Op.iterateLessRef: Op.iterateLessOrEqualRef, Value.integer(body->count), condition->ops[0].text), body); 186 | OpList.destroy(condition), condition = NULL; 187 | OpList.destroy(step), step = NULL; 188 | return OpList.join(initial, body); 189 | } 190 | 191 | if (condition->ops[1].native == Op.getLocal && ( 192 | condition->ops[0].native == Op.more || 193 | condition->ops[0].native == Op.moreOrEqual )) 194 | if (step->count >= 2 && step->ops[1].value.data.key.data.integer == condition->ops[1].value.data.key.data.integer) 195 | { 196 | struct Value stepValue; 197 | if (step->count == 2 && (step->ops[0].native == Op.decrementRef || step->ops[0].native == Op.postDecrementRef)) 198 | stepValue = Value.integer(1); 199 | else if (step->count == 3 && step->ops[0].native == Op.minusAssignRef && step->ops[2].native == Op.value && step->ops[2].value.type == Value(integerType) && step->ops[2].value.data.integer > 0) 200 | stepValue = step->ops[2].value; 201 | else 202 | goto normal; 203 | 204 | if (condition->ops[2].native == Op.getLocal) 205 | body = OpList.unshift(Op.make(Op.getLocalRef, condition->ops[2].value, condition->ops[2].text), body); 206 | else if (condition->ops[2].native == Op.value) 207 | body = OpList.unshift(Op.make(Op.valueConstRef, condition->ops[2].value, condition->ops[2].text), body); 208 | else 209 | goto normal; 210 | 211 | body = OpList.appendNoop(OpList.unshift(Op.make(Op.getLocalRef, condition->ops[1].value, condition->ops[1].text), body)); 212 | body = OpList.unshift(Op.make(Op.value, stepValue, condition->ops[0].text), body); 213 | body = OpList.unshift(Op.make(condition->ops[0].native == Op.more? Op.iterateMoreRef: Op.iterateMoreOrEqualRef, Value.integer(body->count), condition->ops[0].text), body); 214 | OpList.destroy(condition), condition = NULL; 215 | OpList.destroy(step), step = NULL; 216 | return OpList.join(initial, body); 217 | } 218 | } 219 | 220 | normal: 221 | { 222 | int skipOpCount; 223 | 224 | if (!condition) 225 | condition = OpList.create(Op.value, Value.truth(1), Text(empty)); 226 | 227 | if (step) 228 | { 229 | step = OpList.unshift(Op.make(Op.discard, Value(none), Text(empty)), step); 230 | skipOpCount = step->count; 231 | } 232 | else 233 | skipOpCount = 0; 234 | 235 | body = OpList.appendNoop(body); 236 | 237 | if (reverseCondition) 238 | { 239 | skipOpCount += condition->count + body->count; 240 | body = OpList.append(body, Op.make(Op.value, Value.truth(1), Text(empty))); 241 | body = OpList.append(body, Op.make(Op.jump, Value.integer(-body->count - 1), Text(empty))); 242 | } 243 | 244 | body = OpList.join(OpList.join(step, condition), body); 245 | body = OpList.unshift(Op.make(Op.jump, Value.integer(body->count), Text(empty)), body); 246 | initial = OpList.append(initial, Op.make(Op.iterate, Value.integer(skipOpCount), Text(empty))); 247 | return OpList.join(initial, body); 248 | } 249 | } 250 | 251 | void optimizeWithEnvironment (struct OpList *self, struct Object *environment, uint32_t selfIndex) 252 | { 253 | uint32_t index, count, slot, haveLocal = 0, environmentLevel = 0; 254 | struct Key environments[0xff]; 255 | 256 | if (!self) 257 | return; 258 | 259 | for (index = 0, count = self->count; index < count; ++index) 260 | { 261 | if (self->ops[index].native == Op.with) 262 | { 263 | index += self->ops[index].value.data.integer; 264 | haveLocal = 1; 265 | } 266 | 267 | if (self->ops[index].native == Op.function) 268 | { 269 | uint32_t selfIndex = index && self->ops[index - 1].native == Op.setLocalSlot? self->ops[index - 1].value.data.integer: 0; 270 | optimizeWithEnvironment(self->ops[index].value.data.function->oplist, &self->ops[index].value.data.function->environment, selfIndex); 271 | } 272 | 273 | if (self->ops[index].native == Op.pushEnvironment) 274 | environments[environmentLevel++] = self->ops[index].value.data.key; 275 | 276 | if (self->ops[index].native == Op.popEnvironment) 277 | --environmentLevel; 278 | 279 | if (self->ops[index].native == Op.createLocalRef 280 | || self->ops[index].native == Op.getLocalRefOrNull 281 | || self->ops[index].native == Op.getLocalRef 282 | || self->ops[index].native == Op.getLocal 283 | || self->ops[index].native == Op.setLocal 284 | || self->ops[index].native == Op.deleteLocal 285 | ) 286 | { 287 | struct Object *searchEnvironment = environment; 288 | uint32_t level; 289 | 290 | level = environmentLevel; 291 | while (level--) 292 | if (Key.isEqual(environments[level], self->ops[index].value.data.key)) 293 | goto notfound; 294 | 295 | level = environmentLevel; 296 | do 297 | { 298 | for (slot = searchEnvironment->hashmapCount; slot--;) 299 | { 300 | if (searchEnvironment->hashmap[slot].value.check == 1) 301 | { 302 | if (Key.isEqual(searchEnvironment->hashmap[slot].value.key, self->ops[index].value.data.key)) 303 | { 304 | if (!level) 305 | { 306 | self->ops[index] = Op.make( 307 | self->ops[index].native == Op.createLocalRef? Op.getLocalSlotRef: 308 | self->ops[index].native == Op.getLocalRefOrNull? Op.getLocalSlotRef: 309 | self->ops[index].native == Op.getLocalRef? Op.getLocalSlotRef: 310 | self->ops[index].native == Op.getLocal? Op.getLocalSlot: 311 | self->ops[index].native == Op.setLocal? Op.setLocalSlot: 312 | self->ops[index].native == Op.deleteLocal? Op.deleteLocalSlot: NULL 313 | , Value.integer(slot), self->ops[index].text); 314 | } 315 | else if (slot <= INT16_MAX && level <= INT16_MAX) 316 | { 317 | self->ops[index] = Op.make( 318 | self->ops[index].native == Op.createLocalRef? Op.getParentSlotRef: 319 | self->ops[index].native == Op.getLocalRefOrNull? Op.getParentSlotRef: 320 | self->ops[index].native == Op.getLocalRef? Op.getParentSlotRef: 321 | self->ops[index].native == Op.getLocal? Op.getParentSlot: 322 | self->ops[index].native == Op.setLocal? Op.setParentSlot: 323 | self->ops[index].native == Op.deleteLocal? Op.deleteParentSlot: NULL 324 | , Value.integer((level << 16) | slot), self->ops[index].text); 325 | } 326 | else 327 | goto notfound; 328 | 329 | if (index > 1 && level == 1 && slot == selfIndex) 330 | { 331 | struct Op op = self->ops[index - 1]; 332 | if (op.native == Op.call && self->ops[index - 2].native == Op.result) 333 | { 334 | self->ops[index - 1] = Op.make(Op.repopulate, op.value, op.text); 335 | self->ops[index] = Op.make(Op.value, Value.integer(-index - 1), self->ops[index].text); 336 | } 337 | } 338 | 339 | goto found; 340 | } 341 | } 342 | } 343 | 344 | ++level; 345 | } 346 | while (( searchEnvironment = searchEnvironment->prototype )); 347 | 348 | notfound: 349 | haveLocal = 1; 350 | found: 351 | ; 352 | } 353 | } 354 | 355 | if (!haveLocal) 356 | Object.stripMap(environment); 357 | } 358 | 359 | void dumpTo (struct OpList *self, FILE *file) 360 | { 361 | uint32_t i; 362 | 363 | assert(self); 364 | 365 | fputc('\n', stderr); 366 | if (!self) 367 | return; 368 | 369 | for (i = 0; i < self->count; ++i) 370 | { 371 | char c = self->ops[i].text.flags & Text(breakFlag)? i? '!': 'T': '|'; 372 | fprintf(file, "[%p] %c %s ", (void *)(self->ops + i), c, Op.toChars(self->ops[i].native)); 373 | 374 | if (self->ops[i].native == Op.function) 375 | { 376 | fprintf(file, "{"); 377 | OpList.dumpTo(self->ops[i].value.data.function->oplist, stderr); 378 | fprintf(file, "} "); 379 | } 380 | else if (self->ops[i].native == Op.getParentSlot || self->ops[i].native == Op.getParentSlotRef || self->ops[i].native == Op.setParentSlot) 381 | fprintf(file, "[-%hu] %hu", (uint16_t)(self->ops[i].value.data.integer >> 16), (uint16_t)(self->ops[i].value.data.integer & 0xffff)); 382 | else if (self->ops[i].value.type != Value(undefinedType) || self->ops[i].native == Op.value || self->ops[i].native == Op.exchange) 383 | Value.dumpTo(self->ops[i].value, file); 384 | 385 | if (self->ops[i].native == Op.text) 386 | fprintf(file, "'%.*s'", (int)self->ops[i].text.length, self->ops[i].text.bytes); 387 | 388 | if (self->ops[i].text.length) 389 | fprintf(file, " `%.*s`", (int)self->ops[i].text.length, self->ops[i].text.bytes); 390 | 391 | fputc('\n', stderr); 392 | } 393 | } 394 | 395 | struct Text text (struct OpList *oplist) 396 | { 397 | uint16_t length; 398 | if (!oplist) 399 | return Text(empty); 400 | 401 | length = oplist->ops[oplist->count - 1].text.bytes + oplist->ops[oplist->count - 1].text.length - oplist->ops[0].text.bytes; 402 | 403 | return Text.make( 404 | oplist->ops[0].text.bytes, 405 | oplist->ops[0].text.length > length? oplist->ops[0].text.length: length); 406 | } 407 | -------------------------------------------------------------------------------- /src/builtin/json.c: -------------------------------------------------------------------------------- 1 | // 2 | // json.c 3 | // libecc 4 | // 5 | // Copyright (c) 2019 Aurélien Bouilland 6 | // Licensed under MIT license, see LICENSE.txt file in project root 7 | // 8 | 9 | #define Implementation 10 | #include "json.h" 11 | 12 | #include "../ecc.h" 13 | #include "../parser.h" 14 | #include "../oplist.h" 15 | 16 | // MARK: - Private 17 | 18 | struct Parse { 19 | struct Text text; 20 | const char *start; 21 | int line; 22 | 23 | // reviver 24 | 25 | struct Context context; 26 | struct Function * function; 27 | struct Object * arguments; 28 | const struct Op * ops; 29 | }; 30 | 31 | struct Stringify { 32 | struct Chars(Append) chars; 33 | char spaces[11]; 34 | int level; 35 | struct Object *filter; 36 | 37 | // replacer 38 | 39 | struct Context context; 40 | struct Function * function; 41 | struct Object * arguments; 42 | const struct Op * ops; 43 | }; 44 | 45 | const struct Object(Type) JSON(type) = { 46 | .text = &Text(jsonType), 47 | }; 48 | 49 | static 50 | struct Value error (struct Parse *parse, int length, struct Chars *chars) 51 | { 52 | return Value.error(Error.syntaxError(Text.make(parse->text.bytes + (length < 0? length: 0), abs(length)), chars)); 53 | } 54 | 55 | static 56 | struct Text errorOfLine (struct Parse *parse) 57 | { 58 | struct Text text = { 0 }; 59 | text.bytes = parse->start; 60 | while (parse->text.length) 61 | { 62 | struct Text(Char) c = Text.character(parse->text); 63 | if (c.codepoint == '\r' || c.codepoint == '\n') 64 | break; 65 | 66 | Text.advance(&parse->text, 1); 67 | } 68 | text.length = (int32_t)(parse->text.bytes - parse->start); 69 | return text; 70 | } 71 | 72 | static 73 | struct Text(Char) nextc (struct Parse *parse) 74 | { 75 | struct Text(Char) c = { 0 }; 76 | while (parse->text.length) 77 | { 78 | c = Text.nextCharacter(&parse->text); 79 | 80 | if (c.codepoint == '\r' && Text.character(parse->text).codepoint == '\n') 81 | Text.advance(&parse->text, 1); 82 | 83 | if (c.codepoint == '\r' || c.codepoint == '\n') 84 | { 85 | parse->start = parse->text.bytes; 86 | ++parse->line; 87 | } 88 | 89 | if (!isspace(c.codepoint)) 90 | break; 91 | } 92 | return c; 93 | } 94 | 95 | static 96 | struct Text string (struct Parse *parse) 97 | { 98 | struct Text text = { parse->text.bytes }; 99 | struct Text(Char) c; 100 | 101 | do 102 | { 103 | c = Text.nextCharacter(&parse->text); 104 | if (c.codepoint == '\\') 105 | Text.advance(&parse->text, 1); 106 | } 107 | while (c.codepoint != '\"' && parse->text.length); 108 | 109 | text.length = (int32_t)(parse->text.bytes - text.bytes - 1); 110 | return text; 111 | } 112 | 113 | static struct Value object (struct Parse *parse); 114 | static struct Value array (struct Parse *parse); 115 | 116 | static 117 | struct Value literal (struct Parse *parse) 118 | { 119 | struct Text(Char) c = nextc(parse); 120 | 121 | switch (c.codepoint) 122 | { 123 | case 't': 124 | if (!memcmp(parse->text.bytes, "rue", 3)) 125 | { 126 | Text.advance(&parse->text, 3); 127 | return Value(true); 128 | } 129 | break; 130 | 131 | case 'f': 132 | if (!memcmp(parse->text.bytes, "alse", 4)) 133 | { 134 | Text.advance(&parse->text, 4); 135 | return Value(false); 136 | } 137 | break; 138 | 139 | case 'n': 140 | if (!memcmp(parse->text.bytes, "ull", 3)) 141 | { 142 | Text.advance(&parse->text, 3); 143 | return Value(null); 144 | } 145 | break; 146 | 147 | case '-': 148 | case '0': 149 | case '1': 150 | case '2': 151 | case '3': 152 | case '4': 153 | case '5': 154 | case '6': 155 | case '7': 156 | case '8': 157 | case '9': 158 | { 159 | struct Text text = Text.make(parse->text.bytes - 1, 1); 160 | 161 | if (c.codepoint == '-') 162 | c = Text.nextCharacter(&parse->text); 163 | 164 | if (c.codepoint != '0') 165 | while (parse->text.length) 166 | { 167 | c = Text.nextCharacter(&parse->text); 168 | if (!isdigit(c.codepoint)) 169 | break; 170 | } 171 | else 172 | c = Text.nextCharacter(&parse->text); 173 | 174 | if (c.codepoint == '.') 175 | { 176 | while (parse->text.length) 177 | { 178 | c = Text.nextCharacter(&parse->text); 179 | if (!isdigit(c.codepoint)) 180 | break; 181 | } 182 | } 183 | 184 | if (c.codepoint == 'e' || c.codepoint == 'E') 185 | { 186 | c = Text.nextCharacter(&parse->text); 187 | if (c.codepoint == '+' || c.codepoint == '-') 188 | c = Text.nextCharacter(&parse->text); 189 | 190 | while (parse->text.length) 191 | { 192 | c = Text.nextCharacter(&parse->text); 193 | if (!isdigit(c.codepoint)) 194 | break; 195 | } 196 | } 197 | 198 | Text.advance(&parse->text, -c.units); 199 | text.length = (int32_t)(parse->text.bytes - text.bytes); 200 | 201 | return Lexer.scanBinary(text, 0); 202 | } 203 | 204 | case '"': 205 | { 206 | struct Text text = string(parse); 207 | return Value.chars(Chars.createWithBytes(text.length, text.bytes)); 208 | break; 209 | } 210 | 211 | case '{': 212 | return object(parse); 213 | break; 214 | 215 | case '[': 216 | return array(parse); 217 | break; 218 | } 219 | return error(parse, -c.units, Chars.create("unexpected '%.*s'", c.units, parse->text.bytes - c.units)); 220 | } 221 | 222 | static 223 | struct Value object (struct Parse *parse) 224 | { 225 | struct Object *object = Object.create(Object(prototype)); 226 | struct Text(Char) c; 227 | struct Value value; 228 | struct Key key; 229 | 230 | c = nextc(parse); 231 | if (c.codepoint != '}') 232 | for (;;) 233 | { 234 | if (c.codepoint != '"') 235 | return error(parse, -c.units, Chars.create("expect property name")); 236 | 237 | key = Key.makeWithText(string(parse), Key(copyOnCreate)); 238 | 239 | c = nextc(parse); 240 | if (c.codepoint != ':') 241 | return error(parse, -c.units, Chars.create("expect colon")); 242 | 243 | value = literal(parse); 244 | if (value.type == Value(errorType)) 245 | return value; 246 | 247 | Object.addMember(object, key, value, 0); 248 | 249 | c = nextc(parse); 250 | 251 | if (c.codepoint == '}') 252 | break; 253 | else if (c.codepoint == ',') 254 | c = nextc(parse); 255 | else 256 | return error(parse, -c.units, Chars.create("unexpected '%.*s'", c.units, parse->text.bytes - c.units)); 257 | } 258 | 259 | return Value.object(object); 260 | } 261 | 262 | static 263 | struct Value array (struct Parse *parse) 264 | { 265 | struct Object *object = Array.create(); 266 | struct Text(Char) c; 267 | struct Value value; 268 | 269 | for (;;) 270 | { 271 | value = literal(parse); 272 | if (value.type == Value(errorType)) 273 | return value; 274 | 275 | Object.addElement(object, object->elementCount, value, 0); 276 | 277 | c = nextc(parse); 278 | 279 | if (c.codepoint == ',') 280 | continue; 281 | 282 | if (c.codepoint == ']') 283 | break; 284 | 285 | return error(parse, -c.units, Chars.create("unexpected '%.*s'", c.units, parse->text.bytes - c.units)); 286 | } 287 | return Value.object(object); 288 | } 289 | 290 | static 291 | struct Value json (struct Parse *parse) 292 | { 293 | struct Text(Char) c = nextc(parse); 294 | 295 | if (c.codepoint == '{') 296 | return object(parse); 297 | else if (c.codepoint == '[') 298 | return array(parse); 299 | 300 | return error(parse, -c.units, Chars.create("expect { or [")); 301 | } 302 | 303 | // MARK: - Static Members 304 | 305 | static 306 | struct Value revive (struct Parse *parse, struct Value this, struct Value property, struct Value value) 307 | { 308 | uint16_t hashmapCount; 309 | 310 | hashmapCount = parse->context.environment->hashmapCount; 311 | switch (hashmapCount) { 312 | default: 313 | memcpy(parse->context.environment->hashmap + 5, 314 | parse->function->environment.hashmap, 315 | sizeof(*parse->context.environment->hashmap) * (hashmapCount - 5)); 316 | case 5: 317 | parse->context.environment->hashmap[3 + 1].value = value; 318 | case 4: 319 | parse->context.environment->hashmap[3 + 0].value = property; 320 | case 3: 321 | break; 322 | case 2: 323 | case 1: 324 | case 0: 325 | assert(0); 326 | break; 327 | } 328 | 329 | parse->context.ops = parse->ops; 330 | parse->context.this = this; 331 | parse->arguments->element[0].value = property; 332 | parse->arguments->element[1].value = value; 333 | return parse->context.ops->native(&parse->context); 334 | } 335 | 336 | static 337 | struct Value walker (struct Parse *parse, struct Value this, struct Value property, struct Value value) 338 | { 339 | uint32_t index, count; 340 | struct Chars(Append) chars; 341 | 342 | if (Value.isObject(value)) 343 | { 344 | struct Object *object = value.data.object; 345 | 346 | for (index = 0, count = object->elementCount < Object(ElementMax)? object->elementCount: Object(ElementMax); index < count; ++index) 347 | { 348 | if (object->element[index].value.check == 1) 349 | { 350 | Chars.beginAppend(&chars); 351 | Chars.append(&chars, "%d", index); 352 | object->element[index].value = walker(parse, this, Chars.endAppend(&chars), object->element[index].value); 353 | } 354 | } 355 | 356 | for (index = 2; index < object->hashmapCount; ++index) 357 | { 358 | if (object->hashmap[index].value.check == 1) 359 | object->hashmap[index].value = walker(parse, this, Value.key(object->hashmap[index].value.key), object->hashmap[index].value); 360 | } 361 | } 362 | return revive(parse, this, property, value); 363 | } 364 | 365 | static 366 | struct Value jsonParse (struct Context * const context) 367 | { 368 | struct Value value, reviver, result; 369 | struct Parse parse = { 370 | .context = { 371 | .parent = context, 372 | .ecc = context->ecc, 373 | .depth = context->depth + 1, 374 | .textIndex = Context(callIndex), 375 | }, 376 | }; 377 | 378 | value = Value.toString(context, Context.argument(context, 0)); 379 | reviver = Context.argument(context, 1); 380 | 381 | parse.text = Text.make(Value.stringBytes(&value), Value.stringLength(&value)); 382 | parse.start = parse.text.bytes; 383 | parse.line = 1; 384 | parse.function = reviver.type == Value(functionType)? reviver.data.function: NULL; 385 | parse.ops = parse.function? parse.function->oplist->ops: NULL; 386 | 387 | result = json(&parse); 388 | 389 | if (result.type != Value(errorType) && parse.text.length) 390 | { 391 | struct Text(Char) c = Text.character(parse.text); 392 | result = error(&parse, c.units, Chars.create("unexpected '%.*s'", c.units, parse.text.bytes)); 393 | } 394 | 395 | if (result.type == Value(errorType)) 396 | { 397 | Context.setTextIndex(context, Context(noIndex)); 398 | context->ecc->ofLine = parse.line; 399 | context->ecc->ofText = errorOfLine(&parse); 400 | context->ecc->ofInput = "(parse)"; 401 | Context.throw(context, result); 402 | } 403 | 404 | if (parse.function && parse.function->flags & Function(needHeap)) 405 | { 406 | struct Object *environment = Object.copy(&parse.function->environment); 407 | 408 | parse.context.environment = environment; 409 | parse.arguments = Arguments.createSized(2); 410 | ++parse.arguments->referenceCount; 411 | 412 | environment->hashmap[2].value = Value.object(parse.arguments); 413 | 414 | result = walker(&parse, result, Value.text(&Text(empty)), result); 415 | } 416 | else if (parse.function) 417 | { 418 | struct Object environment = parse.function->environment; 419 | struct Object arguments = Object.identity; 420 | union Object(Hashmap) hashmap[parse.function->environment.hashmapCapacity]; 421 | union Object(Element) element[2]; 422 | 423 | memcpy(hashmap, parse.function->environment.hashmap, sizeof(hashmap)); 424 | parse.context.environment = &environment; 425 | parse.arguments = &arguments; 426 | 427 | arguments.element = element; 428 | arguments.elementCount = 2; 429 | environment.hashmap = hashmap; 430 | environment.hashmap[2].value = Value.object(&arguments); 431 | 432 | result = walker(&parse, result, Value.text(&Text(empty)), result); 433 | } 434 | return result; 435 | } 436 | 437 | static 438 | struct Value replace (struct Stringify *stringify, struct Value this, struct Value property, struct Value value) 439 | { 440 | uint16_t hashmapCount; 441 | 442 | hashmapCount = stringify->context.environment->hashmapCount; 443 | switch (hashmapCount) { 444 | default: 445 | memcpy(stringify->context.environment->hashmap + 5, 446 | stringify->function->environment.hashmap, 447 | sizeof(*stringify->context.environment->hashmap) * (hashmapCount - 5)); 448 | case 5: 449 | stringify->context.environment->hashmap[3 + 1].value = value; 450 | case 4: 451 | stringify->context.environment->hashmap[3 + 0].value = property; 452 | case 3: 453 | break; 454 | case 2: 455 | case 1: 456 | case 0: 457 | assert(0); 458 | break; 459 | } 460 | 461 | stringify->context.ops = stringify->ops; 462 | stringify->context.this = this; 463 | stringify->arguments->element[0].value = property; 464 | stringify->arguments->element[1].value = value; 465 | return stringify->context.ops->native(&stringify->context); 466 | } 467 | 468 | static 469 | int stringifyValue (struct Stringify *stringify, struct Value this, struct Value property, struct Value value, int isArray, int addComa) 470 | { 471 | uint32_t index, count; 472 | 473 | if (stringify->function) 474 | value = replace(stringify, this, property, value); 475 | 476 | if (!isArray) 477 | { 478 | if (value.type == Value(undefinedType)) 479 | return 0; 480 | 481 | if (stringify->filter) 482 | { 483 | struct Object *object = stringify->filter; 484 | int found = 0; 485 | 486 | for (index = 0, count = object->elementCount < Object(ElementMax)? object->elementCount: Object(ElementMax); index < count; ++index) 487 | { 488 | if (object->element[index].value.check == 1) 489 | { 490 | if (Value.isTrue(Value.equals(&stringify->context, property, object->element[index].value))) 491 | { 492 | found = 1; 493 | break; 494 | } 495 | } 496 | } 497 | 498 | if (!found) 499 | return 0; 500 | } 501 | } 502 | 503 | if (addComa) 504 | Chars.append(&stringify->chars, ",%s", strlen(stringify->spaces)? "\n": ""); 505 | 506 | for (index = 0, count = stringify->level; index < count; ++index) 507 | Chars.append(&stringify->chars, "%s", stringify->spaces); 508 | 509 | if (!isArray) 510 | Chars.append(&stringify->chars, "\"%.*s\":%s", Value.stringLength(&property), Value.stringBytes(&property), strlen(stringify->spaces)? " ": ""); 511 | 512 | if (value.type == Value(functionType) || value.type == Value(undefinedType)) 513 | Chars.append(&stringify->chars, "null"); 514 | else if (Value.isObject(value)) 515 | { 516 | struct Object *object = value.data.object; 517 | int isArray = Value.objectIsArray(object); 518 | struct Chars(Append) chars; 519 | const struct Text *property; 520 | int hasValue = 0; 521 | 522 | Chars.append(&stringify->chars, "%s%s", isArray? "[": "{", strlen(stringify->spaces)? "\n": ""); 523 | ++stringify->level; 524 | 525 | for (index = 0, count = object->elementCount < Object(ElementMax)? object->elementCount: Object(ElementMax); index < count; ++index) 526 | { 527 | if (object->element[index].value.check == 1) 528 | { 529 | Chars.beginAppend(&chars); 530 | Chars.append(&chars, "%d", index); 531 | hasValue |= stringifyValue(stringify, value, Chars.endAppend(&chars), object->element[index].value, isArray, hasValue); 532 | } 533 | } 534 | 535 | if (!isArray) 536 | { 537 | for (index = 0; index < object->hashmapCount; ++index) 538 | { 539 | if (object->hashmap[index].value.check == 1) 540 | { 541 | property = Key.textOf(object->hashmap[index].value.key); 542 | hasValue |= stringifyValue(stringify, value, Value.text(property), object->hashmap[index].value, isArray, hasValue); 543 | } 544 | } 545 | } 546 | 547 | Chars.append(&stringify->chars, "%s", strlen(stringify->spaces)? "\n": ""); 548 | 549 | --stringify->level; 550 | for (index = 0, count = stringify->level; index < count; ++index) 551 | Chars.append(&stringify->chars, "%s", stringify->spaces); 552 | 553 | Chars.append(&stringify->chars, "%s", isArray? "]": "}"); 554 | } 555 | else 556 | Chars.appendValue(&stringify->chars, &stringify->context, value); 557 | 558 | return 1; 559 | } 560 | 561 | static 562 | struct Value jsonStringify (struct Context * const context) 563 | { 564 | struct Value value, replacer, space; 565 | struct Stringify stringify = { 566 | .context = { 567 | .parent = context, 568 | .ecc = context->ecc, 569 | .depth = context->depth + 1, 570 | .textIndex = Context(callIndex), 571 | }, 572 | .spaces = { 0 }, 573 | }; 574 | 575 | value = Context.argument(context, 0); 576 | replacer = Context.argument(context, 1); 577 | space = Context.argument(context, 2); 578 | 579 | stringify.filter = replacer.type == Value(objectType) && replacer.data.object->type == &Array(type)? replacer.data.object: NULL; 580 | stringify.function = replacer.type == Value(functionType)? replacer.data.function: NULL; 581 | stringify.ops = stringify.function? stringify.function->oplist->ops: NULL; 582 | 583 | if (Value.isString(space)) 584 | snprintf(stringify.spaces, sizeof(stringify.spaces), "%.*s", (int)Value.stringLength(&space), Value.stringBytes(&space)); 585 | else if (Value.isNumber(space)) 586 | { 587 | int i = Value.toInteger(context, space).data.integer; 588 | 589 | if (i < 0) 590 | i = 0; 591 | 592 | if (i > 10) 593 | i = 10; 594 | 595 | while (i--) 596 | strcat(stringify.spaces, " "); 597 | } 598 | 599 | Chars.beginAppend(&stringify.chars); 600 | 601 | if (stringify.function && stringify.function->flags & Function(needHeap)) 602 | { 603 | struct Object *environment = Object.copy(&stringify.function->environment); 604 | 605 | stringify.context.environment = environment; 606 | stringify.arguments = Arguments.createSized(2); 607 | ++stringify.arguments->referenceCount; 608 | 609 | environment->hashmap[2].value = Value.object(stringify.arguments); 610 | 611 | stringifyValue(&stringify, value, Value.text(&Text(empty)), value, 1, 0); 612 | } 613 | else if (stringify.function) 614 | { 615 | struct Object environment = stringify.function->environment; 616 | struct Object arguments = Object.identity; 617 | union Object(Hashmap) hashmap[stringify.function->environment.hashmapCapacity]; 618 | union Object(Element) element[2]; 619 | 620 | memcpy(hashmap, stringify.function->environment.hashmap, sizeof(hashmap)); 621 | stringify.context.environment = &environment; 622 | stringify.arguments = &arguments; 623 | 624 | arguments.element = element; 625 | arguments.elementCount = 2; 626 | environment.hashmap = hashmap; 627 | environment.hashmap[2].value = Value.object(&arguments); 628 | 629 | stringifyValue(&stringify, value, Value.text(&Text(empty)), value, 1, 0); 630 | } 631 | else 632 | stringifyValue(&stringify, value, Value.text(&Text(empty)), value, 1, 0); 633 | 634 | return Chars.endAppend(&stringify.chars); 635 | } 636 | 637 | // MARK: - Public 638 | 639 | struct Object * JSON(object) = NULL; 640 | 641 | void setup () 642 | { 643 | const enum Value(Flags) h = Value(hidden); 644 | 645 | JSON(object) = Object.createTyped(&JSON(type)); 646 | 647 | Function.addToObject(JSON(object), "parse", jsonParse, -1, h); 648 | Function.addToObject(JSON(object), "stringify", jsonStringify, -1, h); 649 | } 650 | 651 | void teardown (void) 652 | { 653 | JSON(object) = NULL; 654 | } 655 | --------------------------------------------------------------------------------