diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/ucode/chunk.h | 39 | ||||
-rw-r--r-- | include/ucode/compiler.h | 117 | ||||
-rw-r--r-- | include/ucode/lexer.h | 175 | ||||
-rw-r--r-- | include/ucode/lib.h | 132 | ||||
-rw-r--r-- | include/ucode/module.h | 53 | ||||
-rw-r--r-- | include/ucode/source.h | 36 | ||||
-rw-r--r-- | include/ucode/types.h | 439 | ||||
-rw-r--r-- | include/ucode/util.h | 158 | ||||
-rw-r--r-- | include/ucode/value.h | 60 | ||||
-rw-r--r-- | include/ucode/vm.h | 137 |
10 files changed, 1346 insertions, 0 deletions
diff --git a/include/ucode/chunk.h b/include/ucode/chunk.h new file mode 100644 index 0000000..458af1f --- /dev/null +++ b/include/ucode/chunk.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CHUNK_H_ +#define __CHUNK_H_ + +#include <stdint.h> +#include <stddef.h> + +#include "value.h" +#include "util.h" +#include "types.h" + +void uc_chunk_init(uc_chunk_t *chunk); +void uc_chunk_free(uc_chunk_t *chunk); +size_t uc_chunk_add(uc_chunk_t *chunk, uint8_t byte, size_t line); + +ssize_t uc_chunk_add_constant(uc_chunk_t *chunk, uc_value_t *value); +uc_value_t *uc_chunk_get_constant(uc_chunk_t *chunk, size_t idx); +void uc_chunk_pop(uc_chunk_t *chunk); + +size_t uc_chunk_debug_get_srcpos(uc_chunk_t *chunk, size_t off); +void uc_chunk_debug_add_variable(uc_chunk_t *chunk, size_t from, size_t to, size_t slot, bool upval, uc_value_t *name); +uc_value_t *uc_chunk_debug_get_variable(uc_chunk_t *chunk, size_t off, size_t slot, bool upval); + +#endif /* __CHUNK_H_ */ diff --git a/include/ucode/compiler.h b/include/ucode/compiler.h new file mode 100644 index 0000000..19fcf37 --- /dev/null +++ b/include/ucode/compiler.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __COMPILER_H_ +#define __COMPILER_H_ + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#ifdef JSONC + #include <json.h> +#else + #include <json-c/json.h> +#endif + +#include "source.h" +#include "lexer.h" +#include "types.h" +#include "util.h" + +typedef enum { + P_NONE, + + P_COMMA, /* , */ + + P_ASSIGN, /* = += -= *= /= %= <<= >>= &= ^= |= */ + + P_TERNARY, /* ?: */ + + P_OR, /* || */ + P_AND, /* && */ + P_BOR, /* | */ + P_BXOR, /* ^ */ + P_BAND, /* & */ + + P_EQUAL, /* === !== == != */ + P_COMPARE, /* < <= > >= in */ + + P_SHIFT, /* << >> */ + + P_ADD, /* + - */ + P_MUL, /* * / % */ + + P_UNARY, /* ! ~ +… -… ++… --… */ + + P_INC, /* …++ …-- */ + + P_CALL, /* ….…, …[…], …(…) */ + + P_PRIMARY /* (…) */ +} uc_precedence_t; + +typedef struct uc_patchlist { + struct uc_patchlist *parent; + size_t depth, count, *entries; +} uc_patchlist_t; + +typedef struct { + uc_value_t *name; + ssize_t depth; + size_t from; + bool captured; + bool constant; +} uc_local_t; + +typedef struct { + uc_value_t *name; + size_t index; + bool local; + bool constant; +} uc_upval_t; + +uc_declare_vector(uc_locals_t, uc_local_t); +uc_declare_vector(uc_upvals_t, uc_upval_t); +uc_declare_vector(uc_jmplist_t, size_t); + +typedef struct { + uc_parse_config_t *config; + uc_lexer_t lex; + uc_token_t prev, curr; + bool synchronizing; + uc_stringbuf_t *error; +} uc_parser_t; + +typedef struct uc_compiler { + struct uc_compiler *parent; + uc_locals_t locals; + uc_upvals_t upvals; + uc_patchlist_t *patchlist; + uc_value_t *function; + uc_parser_t *parser; + size_t scope_depth, current_srcpos, last_insn; +} uc_compiler_t; + +typedef struct { + void (*prefix)(uc_compiler_t *, bool); + void (*infix)(uc_compiler_t *, bool); + uc_precedence_t precedence; +} uc_parse_rule_t; + +uc_function_t *uc_compile(uc_parse_config_t *config, uc_source_t *source, char **errp); + +#endif /* __COMPILER_H_ */ diff --git a/include/ucode/lexer.h b/include/ucode/lexer.h new file mode 100644 index 0000000..1b19b60 --- /dev/null +++ b/include/ucode/lexer.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LEXER_H_ +#define __LEXER_H_ + +#include "source.h" +#include "types.h" + + +typedef enum { + TK_LEXP = 1, + TK_REXP, + TK_LSTM, + TK_RSTM, + TK_IF, + TK_ELSE, + TK_COMMA, + TK_ASSIGN, + TK_ASADD, + TK_ASSUB, + TK_ASMUL, + TK_ASDIV, + TK_ASMOD, + TK_ASLEFT, + TK_ASRIGHT, + TK_ASBAND, + TK_ASBXOR, + TK_ASBOR, + TK_QMARK, + TK_COLON, + TK_OR, + TK_AND, + TK_BOR, + TK_BXOR, + TK_BAND, + TK_EQS, + TK_NES, + TK_EQ, + TK_NE, + TK_LT, + TK_LE, + TK_GT, + TK_GE, + TK_IN, + TK_LSHIFT, + TK_RSHIFT, + TK_ADD, + TK_SUB, + TK_MUL, + TK_DIV, + TK_MOD, + TK_NOT, + TK_COMPL, + TK_INC, + TK_DEC, + TK_DOT, + TK_LBRACK, + TK_RBRACK, + TK_LPAREN, + TK_RPAREN, + TK_TEXT, + TK_LBRACE, + TK_RBRACE, + TK_SCOL, + TK_ENDIF, + TK_ELIF, + TK_WHILE, + TK_ENDWHILE, + TK_FOR, + TK_ENDFOR, + TK_FUNC, + TK_LABEL, + TK_ENDFUNC, + TK_TRY, + TK_CATCH, + TK_SWITCH, + TK_CASE, + TK_DEFAULT, + TK_ELLIP, + TK_RETURN, + TK_BREAK, + TK_CONTINUE, + TK_LOCAL, + TK_ARROW, + TK_TRUE, + TK_FALSE, + TK_NUMBER, + TK_DOUBLE, + TK_STRING, + TK_REGEXP, + TK_NULL, + TK_THIS, + TK_DELETE, + TK_CONST, + + TK_EOF, + TK_ERROR +} uc_tokentype_t; + +typedef enum { + UC_LEX_IDENTIFY_BLOCK, + UC_LEX_BLOCK_COMMENT_START, + UC_LEX_BLOCK_EXPRESSION_START, + UC_LEX_BLOCK_EXPRESSION_EMIT_TAG, + UC_LEX_BLOCK_STATEMENT_START, + UC_LEX_BLOCK_COMMENT, + UC_LEX_IDENTIFY_TOKEN, + UC_LEX_PARSE_TOKEN, + UC_LEX_EOF +} uc_lex_state_t; + +typedef struct { + uc_tokentype_t type; + uc_value_t *uv; + size_t pos; +} uc_token_t; + +typedef struct { + uc_lex_state_t state; + uc_parse_config_t *config; + uc_source_t *source; + uint8_t eof:1; + uint8_t is_escape:1; + uint8_t no_regexp:1; + uint8_t no_keyword:1; + size_t buflen; + char *buf, *bufstart, *bufend; + size_t lookbehindlen; + char *lookbehind; + const void *tok; + uc_token_t curr; + char esc[5]; + uint8_t esclen; + int lead_surrogate; + size_t lastoff; + enum { + UNSPEC, + PLUS, + MINUS, + NEWLINE + } modifier; + enum { + NONE, + EXPRESSION = '{', + STATEMENTS = '%', + COMMENT = '#' + } block; +} uc_lexer_t; + + +void uc_lexer_init(uc_lexer_t *lex, uc_parse_config_t *config, uc_source_t *source); +void uc_lexer_free(uc_lexer_t *lex); + +uc_token_t *uc_lexer_next_token(uc_lexer_t *lex); + +bool utf8enc(char **out, int *rem, int code); + +const char * +uc_get_tokenname(unsigned type); + +#endif /* __LEXER_H_ */ diff --git a/include/ucode/lib.h b/include/ucode/lib.h new file mode 100644 index 0000000..7eeae9f --- /dev/null +++ b/include/ucode/lib.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIB_H_ +#define __LIB_H_ + +#include "vm.h" +#include "lexer.h" + +typedef struct { + const char *name; + uc_cfn_ptr_t func; +} uc_cfunction_list_t; + +extern const uc_cfunction_list_t uc_stdlib_functions[]; + +void uc_load_stdlib(uc_value_t *scope); + +bool format_source_context(uc_stringbuf_t *buf, uc_source_t *src, size_t off, bool compact); +bool format_error_context(uc_stringbuf_t *buf, uc_source_t *src, uc_value_t *stacktrace, size_t off); + + +/* vm helper */ + +static inline void * +_uc_get_self(uc_vm_t *vm, const char *expected_type) +{ + return ucv_ressource_dataptr(vm->callframes.entries[vm->callframes.count - 1].ctx, expected_type); +} + +#define uc_get_self(...) _uc_get_self(vm, __VA_ARGS__) + +static inline uc_value_t * +_uc_get_arg(uc_vm_t *vm, size_t nargs, size_t n) +{ + if (n >= nargs) + return NULL; + + return uc_vm_stack_peek(vm, nargs - n - 1); +} + +#define uc_get_arg(...) _uc_get_arg(vm, nargs, __VA_ARGS__) + +#define uc_call(nargs) uc_vm_call(vm, false, nargs) +#define uc_push_val(val) uc_vm_stack_push(vm, val) +#define uc_pop_val() uc_vm_stack_pop(vm) + + +/* value helper */ + +static inline uc_value_t * +uc_alloc_ressource(uc_ressource_type_t *type, void *data) +{ + return ucv_ressource_new(type, data); +} + +static inline uc_type_t +uc_to_number(uc_value_t *v, int64_t *n, double *d) +{ + return uc_cast_number(v, n, d); +} + +static inline double +uc_to_double(uc_value_t *v) +{ + int64_t n; + double d; + + return (uc_cast_number(v, &n, &d) == UC_DOUBLE) ? d : (double)n; +} + +static inline int64_t +uc_to_int64(uc_value_t *v) +{ + int64_t n; + double d; + + return (uc_cast_number(v, &n, &d) == UC_DOUBLE) ? (int64_t)d : n; +} + + +/* ressource type helper */ + +static inline uc_ressource_type_t * +_uc_declare_type(uc_vm_t *vm, const char *name, const uc_cfunction_list_t *list, size_t len, void (*freefn)(void *)) +{ + uc_value_t *proto = ucv_object_new(NULL); + + while (len-- > 0) + ucv_object_add(proto, list[len].name, + ucv_cfunction_new(list[len].name, list[len].func)); + + return ucv_ressource_type_add(vm, name, proto, freefn); +} + +#define uc_declare_type(vm, name, functions, freefn) \ + _uc_declare_type(vm, name, functions, ARRAY_SIZE(functions), freefn) + + +/* function helper */ + +#define uc_add_function(object, name, function) \ + ucv_object_add(object, name, ucv_cfunction_new(name, function)) + +static inline bool +_uc_add_functions(uc_value_t *object, const uc_cfunction_list_t *list, size_t len) +{ + bool rv = true; + + while (len-- > 0) + rv &= uc_add_function(object, list[len].name, list[len].func); + + return rv; +} + +#define uc_add_functions(object, functions) \ + _uc_add_functions(object, functions, ARRAY_SIZE(functions)) + +#endif /* __LIB_H_ */ diff --git a/include/ucode/module.h b/include/ucode/module.h new file mode 100644 index 0000000..43d9d51 --- /dev/null +++ b/include/ucode/module.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __MODULE_H_ +#define __MODULE_H_ + +#include "lib.h" +#include "vm.h" + +#define register_functions(scope, functions) \ + if (scope) \ + for (int i = 0; i < ARRAY_SIZE(functions); i++) \ + json_object_object_add(scope->header.jso, functions[i].name, \ + ops->value.cfunc(functions[i].name, functions[i].func)) + +#define alloc_prototype(functions) ({ \ + uc_prototype *__proto = uc_object_as_prototype(ops->value.proto(NULL)); \ + register_functions(__proto, functions); \ + __proto; \ +}) + +#define declare_type(name, proto, freefn) \ + ucv_ressource_type_add(name, proto, freefn) + +#define alloc_ressource(data, type) \ + ucv_ressource_new(ucv_object_new(NULL), type, data) + +#define register_ressource(scope, key, res) \ + json_object_object_add((scope)->header.jso, key, (res)->header.jso) + +void uc_module_init(uc_vm_t *vm, uc_value_t *scope) __attribute__((weak)); + +void uc_module_entry(uc_vm_t *vm, uc_value_t *scope); +void uc_module_entry(uc_vm_t *vm, uc_value_t *scope) +{ + if (uc_module_init) + uc_module_init(vm, scope); +} + +#endif /* __MODULE_H_ */ diff --git a/include/ucode/source.h b/include/ucode/source.h new file mode 100644 index 0000000..3de7c93 --- /dev/null +++ b/include/ucode/source.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __SOURCE_H_ +#define __SOURCE_H_ + +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +#include "util.h" +#include "types.h" + + +uc_source_t *uc_source_new_file(const char *path); +uc_source_t *uc_source_new_buffer(const char *name, char *buf, size_t len); + +size_t uc_source_get_line(uc_source_t *source, size_t *offset); + +uc_source_t *uc_source_get(uc_source_t *source); +void uc_source_put(uc_source_t *source); + +#endif /* __SOURCE_H_ */ diff --git a/include/ucode/types.h b/include/ucode/types.h new file mode 100644 index 0000000..e2cb15d --- /dev/null +++ b/include/ucode/types.h @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __TYPES_H_ +#define __TYPES_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <regex.h> +#include <json-c/json.h> + +#include "util.h" + + +/* Value types and generic value header */ + +typedef enum uc_type { + UC_NULL, + UC_INTEGER, + UC_BOOLEAN, + UC_STRING, + UC_DOUBLE, + UC_ARRAY, + UC_OBJECT, + UC_REGEXP, + UC_FUNCTION, + UC_CFUNCTION, + UC_CLOSURE, + UC_UPVALUE, + UC_RESSOURCE +} uc_type_t; + +typedef struct uc_value { + uint32_t type:4; + uint32_t mark:1; + uint32_t u64:1; + uint32_t refcount:26; +} uc_value_t; + + +/* Constant list defintions */ + +typedef struct { + size_t isize; + size_t dsize; + uint64_t *index; + char *data; +} uc_value_list_t; + + +/* Source buffer defintions */ + +uc_declare_vector(uc_lineinfo_t, uint8_t); + +typedef struct { + char *filename, *buffer; + FILE *fp; + size_t usecount, off; + uc_lineinfo_t lineinfo; +} uc_source_t; + + +/* Bytecode chunk defintions */ + +typedef struct { + size_t from, to, target, slot; +} uc_ehrange_t; + +typedef struct { + size_t from, to, slot, nameidx; +} uc_varrange_t; + +uc_declare_vector(uc_ehranges_t, uc_ehrange_t); +uc_declare_vector(uc_variables_t, uc_varrange_t); +uc_declare_vector(uc_offsetinfo_t, uint8_t); + +typedef struct { + size_t count; + uint8_t *entries; + uc_value_list_t constants; + uc_ehranges_t ehranges; + struct { + uc_variables_t variables; + uc_value_list_t varnames; + uc_offsetinfo_t offsets; + } debuginfo; +} uc_chunk_t; + + +/* Value type structures */ + +typedef struct uc_weakref { + struct uc_weakref *prev; + struct uc_weakref *next; +} uc_weakref_t; + +typedef struct { + uc_value_t header; + double dbl; +} uc_double_t; + +typedef struct { + uc_value_t header; + union { + int64_t s64; + uint64_t u64; + } i; +} uc_integer_t; + +typedef struct { + uc_value_t header; + size_t length; + char str[]; +} uc_string_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + size_t count; + uc_value_t *proto; + uc_value_t **entries; +} uc_array_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + uc_value_t *proto; + struct lh_table *table; +} uc_object_t; + +typedef struct { + uc_value_t header; + regex_t regexp; + bool icase, newline, global; + char source[]; +} uc_regexp_t; + +typedef struct { + uc_value_t header; + bool arrow, vararg, strict; + size_t nargs; + size_t nupvals; + size_t srcpos; + uc_chunk_t chunk; + uc_source_t *source; + char name[]; +} uc_function_t; + +typedef struct uc_upval_tref { + uc_value_t header; + size_t slot; + bool closed; + uc_value_t *value; + struct uc_upval_tref *next; +} uc_upval_tref_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + bool is_arrow; + uc_function_t *function; + uc_upval_tref_t **upvals; +} uc_closure_t; + +typedef struct uc_vm uc_vm_t; +typedef uc_value_t *(*uc_cfn_ptr_t)(uc_vm_t *, size_t); + +typedef struct { + uc_value_t header; + uc_cfn_ptr_t cfn; + char name[]; +} uc_cfunction_t; + +typedef struct { + const char *name; + uc_value_t *proto; + void (*free)(void *); +} uc_ressource_type_t; + +typedef struct { + uc_value_t header; + uc_ressource_type_t *type; + void *data; +} uc_ressource_t; + +uc_declare_vector(uc_ressource_types_t, uc_ressource_type_t *); + + +/* Parser definitions */ + +typedef struct { + bool lstrip_blocks; + bool trim_blocks; + bool strict_declarations; + bool raw_mode; +} uc_parse_config_t; + + +/* VM definitions */ + +typedef enum { + EXCEPTION_NONE, + EXCEPTION_SYNTAX, + EXCEPTION_RUNTIME, + EXCEPTION_TYPE, + EXCEPTION_REFERENCE, + EXCEPTION_USER, + EXCEPTION_EXIT +} uc_exception_type_t; + +typedef struct { + uc_exception_type_t type; + uc_value_t *stacktrace; + char *message; +} uc_exception_t; + +typedef struct { + uint8_t *ip; + uc_closure_t *closure; + uc_cfunction_t *cfunction; + size_t stackframe; + uc_value_t *ctx; + bool mcall, strict; +} uc_callframe_t; + +uc_declare_vector(uc_callframes_t, uc_callframe_t); +uc_declare_vector(uc_stack_t, uc_value_t *); + +typedef struct printbuf uc_stringbuf_t; + +typedef void (uc_exception_handler_t)(uc_vm_t *, uc_exception_t *); + +struct uc_vm { + uc_stack_t stack; + uc_exception_t exception; + uc_callframes_t callframes; + uc_upval_tref_t *open_upvals; + uc_parse_config_t *config; + uc_value_t *globals; + uc_source_t *sources; + uc_weakref_t values; + uc_ressource_types_t restypes; + union { + uint32_t u32; + int32_t s32; + uint16_t u16; + int16_t s16; + uint8_t u8; + int8_t s8; + } arg; + size_t spread_values; + uint8_t trace; + uc_stringbuf_t *strbuf; + uc_exception_handler_t *exhandler; + FILE *output; +}; + + +/* Value API */ + +void ucv_free(uc_value_t *, bool); +void ucv_put(uc_value_t *); + +uc_value_t *ucv_get(uc_value_t *uv); + +uc_type_t ucv_type(uc_value_t *); +const char *ucv_typename(uc_value_t *); + +uc_value_t *ucv_boolean_new(bool); +bool ucv_boolean_get(uc_value_t *); + +uc_value_t *ucv_string_new(const char *); +uc_value_t *ucv_string_new_length(const char *, size_t); +size_t ucv_string_length(uc_value_t *); + +char *_ucv_string_get(uc_value_t **); +#define ucv_string_get(uv) _ucv_string_get((uc_value_t **)&uv) + +uc_stringbuf_t *ucv_stringbuf_new(void); +uc_value_t *ucv_stringbuf_finish(uc_stringbuf_t *); + +void _ucv_stringbuf_append(uc_stringbuf_t *, const char *, size_t); + +#define _ucv_is_literal(str) ("" str) +#define ucv_stringbuf_append(buf, str) _ucv_stringbuf_append(buf, _ucv_is_literal(str), sizeof(str) - 1) +#define ucv_stringbuf_addstr(buf, str, len) _ucv_stringbuf_append(buf, str, len) +#define ucv_stringbuf_printf(buf, fmt, ...) sprintbuf(buf, fmt, __VA_ARGS__) + +uc_value_t *ucv_int64_new(int64_t); +uc_value_t *ucv_uint64_new(uint64_t); +int64_t ucv_int64_get(uc_value_t *); +uint64_t ucv_uint64_get(uc_value_t *); + +uc_value_t *ucv_double_new(double); +double ucv_double_get(uc_value_t *); + +uc_value_t *ucv_array_new(uc_vm_t *); +uc_value_t *ucv_array_new_length(uc_vm_t *, size_t); +uc_value_t *ucv_array_get(uc_value_t *, size_t); +uc_value_t *ucv_array_pop(uc_value_t *); +uc_value_t *ucv_array_push(uc_value_t *, uc_value_t *); +uc_value_t *ucv_array_shift(uc_value_t *); +uc_value_t *ucv_array_unshift(uc_value_t *, uc_value_t *); +void ucv_array_sort(uc_value_t *, int (*)(const void *, const void *)); +bool ucv_array_delete(uc_value_t *, size_t, size_t); +bool ucv_array_set(uc_value_t *, size_t, uc_value_t *); +size_t ucv_array_length(uc_value_t *); + +uc_value_t *ucv_object_new(uc_vm_t *); +uc_value_t *ucv_object_get(uc_value_t *, const char *, bool *); +bool ucv_object_add(uc_value_t *, const char *, uc_value_t *); +bool ucv_object_delete(uc_value_t *, const char *); +size_t ucv_object_length(uc_value_t *); + +#define ucv_object_foreach(obj, key, val) \ + char *key = NULL; \ + uc_value_t *val = NULL; \ + struct lh_entry *entry##key; \ + struct lh_entry *entry_next##key = NULL; \ + for (entry##key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL; \ + (entry##key ? (key = (char *)lh_entry_k(entry##key), \ + val = (uc_value_t *)lh_entry_v(entry##key), \ + entry_next##key = entry##key->next, entry##key) \ + : 0); \ + entry##key = entry_next##key) + +uc_value_t *ucv_function_new(const char *, size_t, uc_source_t *); +size_t ucv_function_srcpos(uc_value_t *, size_t); + +uc_value_t *ucv_cfunction_new(const char *, uc_cfn_ptr_t); + +uc_value_t *ucv_closure_new(uc_vm_t *, uc_function_t *, bool); + +uc_ressource_type_t *ucv_ressource_type_add(uc_vm_t *, const char *, uc_value_t *, void (*)(void *)); +uc_ressource_type_t *ucv_ressource_type_lookup(uc_vm_t *, const char *); + +uc_value_t *ucv_ressource_new(uc_ressource_type_t *, void *); +void **ucv_ressource_dataptr(uc_value_t *, const char *); + +uc_value_t *ucv_regexp_new(const char *, bool, bool, bool, char **); + +uc_value_t *ucv_upvalref_new(size_t); + +uc_value_t *ucv_prototype_get(uc_value_t *); +bool ucv_prototype_set(uc_value_t *, uc_value_t *); + +uc_value_t *ucv_property_get(uc_value_t *, const char *); + +uc_value_t *ucv_from_json(uc_vm_t *, json_object *); +json_object *ucv_to_json(uc_value_t *); + +char *ucv_to_string(uc_vm_t *, uc_value_t *); +char *ucv_to_jsonstring_formatted(uc_vm_t *, uc_value_t *, char, size_t); +void ucv_to_stringbuf_formatted(uc_vm_t *, uc_stringbuf_t *, uc_value_t *, size_t, char, size_t); + +#define ucv_to_jsonstring(vm, val) ucv_to_jsonstring_formatted(vm, val, '\1', 0) +#define ucv_to_stringbuf(vm, buf, val, json) ucv_to_stringbuf_formatted(vm, buf, val, 0, json ? '\1' : '\0', 0) + +static inline bool +ucv_is_callable(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_CLOSURE: + case UC_CFUNCTION: + return true; + + default: + return false; + } +} + +static inline bool +ucv_is_arrowfn(uc_value_t *uv) +{ + uc_closure_t *closure = (uc_closure_t *)uv; + + return (ucv_type(uv) == UC_CLOSURE && closure->is_arrow); +} + +static inline bool +ucv_is_u64(uc_value_t *uv) +{ + return (((uintptr_t)uv & 3) == 0 && uv != NULL && uv->u64 == true); +} + +static inline bool +ucv_is_scalar(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_NULL: + case UC_BOOLEAN: + case UC_DOUBLE: + case UC_INTEGER: + case UC_STRING: + return true; + + default: + return false; + } +} + +static inline bool +ucv_is_marked(uc_value_t *uv) +{ + return (((uintptr_t)uv & 3) == 0 && uv != NULL && uv->mark == true); +} + +static inline void +ucv_set_mark(uc_value_t *uv) +{ + if (((uintptr_t)uv & 3) == 0 && uv != NULL) + uv->mark = true; +} + +static inline void +ucv_clear_mark(uc_value_t *uv) +{ + if (((uintptr_t)uv & 3) == 0 && uv != NULL) + uv->mark = false; +} + +bool ucv_equal(uc_value_t *, uc_value_t *); + +void ucv_gc(uc_vm_t *, bool); + +#endif /* __TYPES_H_ */ diff --git a/include/ucode/util.h b/include/ucode/util.h new file mode 100644 index 0000000..858a3fd --- /dev/null +++ b/include/ucode/util.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __UTIL_H_ +#define __UTIL_H_ + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> /* va_start(), va_end(), va_list */ +#include <string.h> /* strdup() */ +#include <json-c/json.h> + + +/* alignment & array size */ + +#ifndef ALIGN +#define ALIGN(x) (((x) + sizeof(size_t) - 1) & -sizeof(size_t)) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + + +/* vector macros */ + +#define UC_VECTOR_CHUNK_SIZE 8 + +#define uc_declare_vector(name, type) \ + typedef struct { \ + size_t count; \ + type *entries; \ + } name + +#define uc_vector_grow(vec) \ + do { \ + if (((vec)->count % UC_VECTOR_CHUNK_SIZE) == 0) { \ + (vec)->entries = xrealloc((vec)->entries, sizeof((vec)->entries[0]) * ((vec)->count + UC_VECTOR_CHUNK_SIZE)); \ + memset(&(vec)->entries[(vec)->count], 0, sizeof((vec)->entries[0]) * UC_VECTOR_CHUNK_SIZE); \ + } \ + } while(0) + +#define uc_vector_clear(vec) \ + do { \ + free((vec)->entries); \ + (vec)->entries = NULL; \ + (vec)->count = 0; \ + } while(0) + +#define uc_vector_first(vec) \ + (&((vec)->entries[0])) + +#define uc_vector_last(vec) \ + (&((vec)->entries[(vec)->count - 1])) + + +/* "failsafe" utility functions */ + +static inline void *xalloc(size_t size) { + void *ptr = calloc(1, size); + + if (!ptr) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return ptr; +} + +static inline void *xrealloc(void *ptr, size_t size) { + ptr = realloc(ptr, size); + + if (!ptr) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return ptr; +} + +static inline char *xstrdup(const char *s) { + char *ptr = strdup(s); + + if (!ptr) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return ptr; +} + +static inline struct json_tokener *xjs_new_tokener(void) { + struct json_tokener *tok = json_tokener_new(); + + if (!tok) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return tok; +} + +__attribute__((format(printf, 2, 0))) +static inline int xasprintf(char **strp, const char *fmt, ...) { + va_list ap; + int len; + + va_start(ap, fmt); + len = vasprintf(strp, fmt, ap); + va_end(ap); + + if (len == -1) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return len; +} + +__attribute__((format(printf, 2, 0))) +static inline int xvasprintf(char **strp, const char *fmt, va_list ap) { + int len = vasprintf(strp, fmt, ap); + + if (len == -1) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return len; +} + +static inline struct printbuf *xprintbuf_new(void) { + struct printbuf *pb = printbuf_new(); + + if (!pb) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return pb; +} + +#endif /* __UTIL_H_ */ diff --git a/include/ucode/value.h b/include/ucode/value.h new file mode 100644 index 0000000..04d37a9 --- /dev/null +++ b/include/ucode/value.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __VALUE_H_ +#define __VALUE_H_ + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +#ifdef JSONC + #include <json.h> +#else + #include <json-c/json.h> +#endif + +#include <stdio.h> + +#include "types.h" + +typedef enum { + TAG_INVAL = 0, + TAG_NUM = 1, + TAG_LNUM = 2, + TAG_DBL = 3, + TAG_STR = 4, + TAG_LSTR = 5, + TAG_PTR = 6 +} uc_value_type_t; + +bool uc_cmp(int how, uc_value_t *v1, uc_value_t *v2); +bool uc_val_is_truish(uc_value_t *val); + +uc_type_t uc_cast_number(uc_value_t *v, int64_t *n, double *d); + +uc_value_t *uc_getval(uc_vm_t *, uc_value_t *scope, uc_value_t *key); +uc_value_t *uc_setval(uc_vm_t *, uc_value_t *scope, uc_value_t *key, uc_value_t *val); +bool uc_delval(uc_vm_t *, uc_value_t *scope, uc_value_t *key); + +void uc_vallist_init(uc_value_list_t *list); +void uc_vallist_free(uc_value_list_t *list); + +ssize_t uc_vallist_add(uc_value_list_t *list, uc_value_t *value); +uc_value_type_t uc_vallist_type(uc_value_list_t *list, size_t idx); +uc_value_t *uc_vallist_get(uc_value_list_t *list, size_t idx); + +#endif /* __VALUE_H_ */ diff --git a/include/ucode/vm.h b/include/ucode/vm.h new file mode 100644 index 0000000..553cf61 --- /dev/null +++ b/include/ucode/vm.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __VM_H_ +#define __VM_H_ + +#include <stdbool.h> +#include <stdarg.h> + +#include "chunk.h" +#include "util.h" +#include "lexer.h" +#include "types.h" + +#define __insns \ +__insn(NOOP) \ +__insn(LOAD) \ +__insn(LOAD8) \ +__insn(LOAD16) \ +__insn(LOAD32) \ +__insn(LTHIS) \ +__insn(LREXP) \ +__insn(LNULL) \ +__insn(LTRUE) \ +__insn(LFALSE) \ +__insn(LLOC) \ +__insn(LUPV) \ +__insn(LVAR) \ +__insn(LVAL) \ +__insn(CLFN) \ +__insn(ARFN) \ +__insn(SLOC) \ +__insn(SUPV) \ +__insn(SVAR) \ +__insn(SVAL) \ +__insn(ULOC) \ +__insn(UUPV) \ +__insn(UVAR) \ +__insn(UVAL) \ +__insn(NARR) \ +__insn(PARR) \ +__insn(MARR) \ +__insn(NOBJ) \ +__insn(SOBJ) \ +__insn(MOBJ) \ +__insn(BOR) \ +__insn(BXOR) \ +__insn(BAND) \ +__insn(EQS) \ +__insn(NES) \ +__insn(EQ) \ +__insn(NE) \ +__insn(LT) \ +__insn(LE) \ +__insn(GT) \ +__insn(GE) \ +__insn(IN) \ +__insn(LSHIFT) \ +__insn(RSHIFT) \ +__insn(ADD) \ +__insn(SUB) \ +__insn(MUL) \ +__insn(DIV) \ +__insn(MOD) \ +__insn(NOT) \ +__insn(COMPL) \ +__insn(PLUS) \ +__insn(MINUS) \ +__insn(JMP) \ +__insn(JMPZ) \ +__insn(COPY) \ +__insn(POP) \ +__insn(CUPV) \ +__insn(RETURN) \ +__insn(CALL) \ +__insn(MCALL) \ +__insn(PRINT) \ +__insn(NEXTK) \ +__insn(NEXTKV) \ +__insn(DELETE) + + +#undef __insn +#define __insn(_name) I_##_name, + +typedef enum { + __insns + __I_MAX +} uc_vm_insn_t; + +typedef enum { + STATUS_OK, + STATUS_EXIT, + ERROR_COMPILE, + ERROR_RUNTIME +} uc_vm_status_t; + +extern uint32_t insns[__I_MAX]; + +void uc_vm_init(uc_vm_t *vm, uc_parse_config_t *config); +void uc_vm_free(uc_vm_t *vm); + +uc_value_t *uc_vm_scope_get(uc_vm_t *vm); +void uc_vm_scope_set(uc_vm_t *vm, uc_value_t *ctx); + +void uc_vm_stack_push(uc_vm_t *vm, uc_value_t *value); +uc_value_t *uc_vm_stack_pop(uc_vm_t *vm); +uc_value_t *uc_vm_stack_peek(uc_vm_t *vm, size_t offset); + +uc_exception_handler_t *uc_vm_exception_handler_get(uc_vm_t *vm); +void uc_vm_exception_handler_set(uc_vm_t *vm, uc_exception_handler_t *exhandler); + +uint32_t uc_vm_trace_get(uc_vm_t *vm); +void uc_vm_trace_set(uc_vm_t *vm, uint32_t level); + +uc_exception_type_t uc_vm_call(uc_vm_t *vm, bool mcall, size_t nargs); + +void __attribute__((format(printf, 3, 0))) +uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ...); + +uc_vm_status_t uc_vm_execute(uc_vm_t *vm, uc_function_t *fn, uc_value_t **retval); +uc_value_t *uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...); + +#endif /* __VM_H_ */ |