#ifndef BARE_MODULE_LEXER_H
#define BARE_MODULE_LEXER_H
#include <assert.h>
#include <js.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <utf.h>
enum {
bare_module_lexer_require = 0x1,
bare_module_lexer_import = 0x2,
bare_module_lexer_dynamic = 0x4,
bare_module_lexer_addon = 0x8,
bare_module_lexer_asset = 0x10,
bare_module_lexer_resolve = 0x20,
bare_module_lexer_reexport = 0x40,
};
static inline bool
bare_module_lexer__is_line_terminator(uint8_t c) {
return c == 0xa || c == 0xd;
}
static inline bool
bare_module_lexer__is_whitespace(uint8_t c) {
return c == ' ' || c == '\t' || c == 0xb || c == 0xc || c == 0xa0 || bare_module_lexer__is_line_terminator(c);
}
static inline bool
bare_module_lexer__is_id_start(uint8_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$';
}
static inline bool
bare_module_lexer__is_id(uint8_t c) {
return bare_module_lexer__is_id_start(c) || (c >= '0' && c <= '9');
}
static inline int
bare_module_lexer__add_position(js_env_t *env, js_value_t *entry, size_t statement_start, size_t input_start, size_t input_end) {
int err;
js_value_t *position;
err = js_create_array_with_length(env, 3, &position);
assert(err == 0);
#define V(i, n) \
{ \
js_value_t *val; \
err = js_create_int64(env, n, &val); \
assert(err == 0); \
err = js_set_element(env, position, i, val); \
assert(err == 0); \
}
V(0, statement_start);
V(1, input_start);
V(2, input_end);
#undef V
err = js_set_named_property(env, entry, "position", position);
assert(err == 0);
return 0;
}
static inline int
bare_module_lexer__add_import(js_env_t *env, js_value_t *imports, uint32_t *i, const utf8_t *source, size_t import_start, size_t specifier_start, size_t specifier_end, int type, js_value_t *names, js_value_t *attributes) {
assert(specifier_end >= specifier_start);
int err;
js_value_t *entry;
err = js_create_object(env, &entry);
assert(err == 0);
err = js_set_element(env, imports, *i, entry);
assert(err == 0);
#define V(key, fn, ...) \
{ \
js_value_t *val; \
err = fn(env, __VA_ARGS__, &val); \
assert(err == 0); \
err = js_set_named_property(env, entry, key, val); \
assert(err == 0); \
}
V("specifier", js_create_string_utf8, &source[specifier_start], specifier_end - specifier_start);
V("type", js_create_uint32, type);
#undef V
if (names == NULL) {
err = js_create_array(env, &names);
assert(err == 0);
}
err = js_set_named_property(env, entry, "names", names);
assert(err == 0);
if (attributes == NULL) {
err = js_create_object(env, &attributes);
assert(err == 0);
}
err = js_set_named_property(env, entry, "attributes", attributes);
assert(err == 0);
err = bare_module_lexer__add_position(env, entry, import_start, specifier_start, specifier_end);
assert(err == 0);
*i += 1;
return 0;
}
static inline int
bare_module_lexer__add_export(js_env_t *env, js_value_t *exports, uint32_t *i, const utf8_t *source, size_t export_start, size_t name_start, size_t name_end) {
assert(name_end >= name_start);
int err;
js_value_t *entry;
err = js_create_object(env, &entry);
assert(err == 0);
err = js_set_element(env, exports, *i, entry);
assert(err == 0);
#define V(key, fn, ...) \
{ \
js_value_t *val; \
err = fn(env, __VA_ARGS__, &val); \
assert(err == 0); \
err = js_set_named_property(env, entry, key, val); \
assert(err == 0); \
}
V("name", js_create_string_utf8, &source[name_start], name_end - name_start);
#undef V
err = bare_module_lexer__add_position(env, entry, export_start, name_start, name_end);
assert(err == 0);
*i += 1;
return 0;
}
static inline int
bare_module_lexer__add_name(js_env_t *env, js_value_t **names, uint32_t *i, const utf8_t *name, size_t name_len) {
int err;
if (*names == NULL) {
err = js_create_array(env, names);
assert(err == 0);
*i = 0;
}
js_value_t *string;
err = js_create_string_utf8(env, name, name_len, &string);
assert(err == 0);
err = js_set_element(env, *names, *i, string);
assert(err == 0);
*i += 1;
return 0;
}
static inline int
bare_module_lexer__add_attribute(js_env_t *env, js_value_t **attributes, const utf8_t *key, size_t key_len, const utf8_t *value, size_t value_len) {
int err;
if (*attributes == NULL) {
err = js_create_object(env, attributes);
assert(err == 0);
}
js_value_t *property;
err = js_create_property_key_utf8(env, key, key_len, &property);
assert(err == 0);
js_value_t *string;
err = js_create_string_utf8(env, value, value_len, &string);
assert(err == 0);
err = js_set_property(env, *attributes, property, string);
assert(err == 0);
return 0;
}
// Current character, unchecked
#define u(offset) ((uint8_t) s[i + offset])
// Current character, checked
#define c(offset) (i + offset < n ? u(offset) : -1)
// Begins with string, unchecked
#define bu(t, l) (strncmp((const char *) &s[i], t, l) == 0)
// Begins with string, checked
#define bc(t, l) (i + l < n && bu(t, l))
#define lt bare_module_lexer__is_line_terminator
#define ws bare_module_lexer__is_whitespace
#define ids bare_module_lexer__is_id_start
#define id bare_module_lexer__is_id
static inline int
bare_module_lexer__lex_regex(js_env_t *env, const utf8_t *s, size_t n, size_t i, size_t *result) {
i++;
bool in_character_class = false;
while (i < n) {
if (in_character_class) {
if (u(0) == ']') {
in_character_class = false;
} else if (c(0) == '\\') {
i++;
}
} else if (u(0) == '/') {
i++;
while (i < n && u(0) >= 'a' && u(0) <= 'z') i++;
*result = i;
return 0;
} else if (u(0) == '[') {
in_character_class = true;
} else if (u(0) == '\\') {
i++;
} else if (u(0) == ';' || lt(u(0))) {
return -1;
}
i++;
}
return -1;
}
static inline int
bare_module_lexer__lex_import_attributes(js_env_t *env, js_value_t **attributes, const utf8_t *s, size_t n, size_t i, size_t *result) {
int err;
size_t ks; // Key start
size_t ke; // Key end
size_t vs; // Value start
size_t ve; // Value end
while (i < n && ws(u(0))) i++;
if (c(0) == '{') {
i++;
while (c(0) != '}') {
while (i < n && ws(u(0))) i++;
if (ids(c(0))) {
ks = i++;
while (id(c(0))) i++;
ke = i;
} else if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ks = ++i;
while (i < n && u(0) != e) i++;
if (c(0) != e) break;
ke = i++;
} else break;
while (i < n && ws(u(0))) i++;
if (c(0) != ':') break;
i++;
while (i < n && ws(u(0))) i++;
if (c(0) != '\'' && c(0) != '"') break;
utf8_t e = u(0);
vs = ++i;
while (i < n && u(0) != e) i++;
if (c(0) != e) break;
ve = i++;
err = bare_module_lexer__add_attribute(env, attributes, &s[ks], ke - ks, &s[vs], ve - vs);
if (err < 0) return err;
if (c(0) != ',') break;
i++;
}
}
*result = i;
return 0;
}
static inline int
bare_module_lexer__lex(js_env_t *env, js_value_t *imports, js_value_t *exports, const utf8_t *s, size_t n) {
int err;
size_t i = 0;
size_t is; // Import start
uint32_t il = 0; // Import count
size_t es; // Export start
uint32_t el = 0; // Export count
size_t ss; // Source start
size_t se; // Source end
int type;
js_value_t *names;
uint32_t nl; // Names count
js_value_t *attributes;
while (i < n) {
while (i < n && ws(u(0))) i++;
if (i + 8 >= n) break;
if (bu("//", 2)) {
i += 2;
while (i < n && !lt(u(0))) i++;
if (lt(c(0))) i++;
continue;
}
if (bu("/*", 2)) {
i += 2;
while (i + 1 < n && !bu("*/", 2)) i++;
if (bc("*/", 2)) i += 2;
continue;
}
if (u(0) == '\'' || u(0) == '"' || u(0) == '`') {
utf8_t e = u(0);
i++;
while (i < n) {
if (u(0) == '\\') i += 2;
else if (u(0) != e) i++;
else break;
}
if (c(0) == e) i++;
continue;
}
if (u(0) == '/') {
err = bare_module_lexer__lex_regex(env, s, n, i, &i);
if (err < 0) i++;
continue;
}
type = 0;
names = NULL;
attributes = NULL;
if (bu("require", 7)) {
is = i;
i += 7;
goto require;
}
else if (bu("import", 6)) {
type |= bare_module_lexer_import;
is = i;
i += 6;
while (i < n && ws(u(0))) i++;
// import \*
if (c(0) == '*') {
i++;
while (i < n && ws(u(0))) i++;
// import \* as
if (i + 3 < n && bu("as", 2) && ws(u(2))) {
i += 3;
while (i < n && ws(u(0))) i++;
// import \* as [^\s]+
while (i < n && !ws(u(0))) i++;
while (i < n && ws(u(0))) i++;
// import \* as [^\s]+ from
if (bc("from", 4)) {
i += 4;
err = bare_module_lexer__add_name(env, &names, &nl, (const utf8_t *) "*", -1);
assert(err == 0);
goto from;
}
}
}
// import {
else if (c(0) == '{') {
i++;
// import {[^}]*
while (i < n && u(0) != '}') i++;
// import {[^}]*}
if (c(0) == '}') {
i++;
while (i < n && ws(u(0))) i++;
// import {[^}]*} from
if (bc("from", 4)) {
i += 4;
goto from;
}
}
}
// import ['"]
else if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// import ['"].*['"]
if (c(0) == e) {
se = i;
i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
// import\(
else if (c(0) == '(') {
type |= bare_module_lexer_dynamic;
i++;
while (i < n && ws(u(0))) i++;
// import\(['"]
if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// import\(['"].*['"]
if (c(0) == e) {
se = i;
i++;
while (i < n && ws(u(0))) i++;
while (i < n && u(0) != ')') i++;
// import\(['"].*['"][^)]*\)
if (c(0) == ')') {
i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
}
}
// import\.
else if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
// import\.meta
if (bc("meta", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
// import\.meta\.
if (c(0) == '.') {
i++;
// import\.meta\.a
if (i + 5 < n && u(0) == 'a') {
i++;
// import\.meta\.addon
if (bu("ddon", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_addon;
// import\.meta\.addon\.
if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
// import\.meta\.addon\.resolve
if (i + 7 < n && bu("resolve", 7)) {
i += 7;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_resolve;
}
else continue;
}
}
// import\.meta\.asset
else if (bu("sset", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_asset;
}
}
// import\.meta\.resolve
else if (i + 7 < n && bu("resolve", 7)) {
i += 7;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_resolve;
}
else continue;
// import\.meta\.(resolve|addon(\.resolve)?|asset)\(
if (c(0) == '(') {
i++;
while (i < n && ws(u(0))) i++;
// import\.meta\.(resolve|addon(\.resolve)?|asset)\(['"]
if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// import\.meta\.(resolve|addon(\.resolve)?|asset)\(['"].*['"]
if (c(0) == e) {
se = i;
i++;
while (i < n && u(0) != ')') i++;
// import\.meta\.(resolve|addon(\.resolve)?|asset)\(['"].*['"][^)]*\)
if (c(0) == ')') {
i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
}
// import\.meta\.addon(\.resolve)?\(\)
else if (c(0) == ')' && (type & bare_module_lexer_addon)) {
ss = se = i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
}
}
}
// import\s
else if (ws(c(-1))) {
size_t j = i;
// import [^\s,]+
while (i < n && !ws(u(0)) && u(0) != ',') i++;
if (j < i) {
while (i < n && ws(u(0))) i++;
// import [^\s,]+ from
if (bc("from", 4)) {
i += 4;
err = bare_module_lexer__add_name(env, &names, &nl, (const utf8_t *) "default", -1);
assert(err == 0);
goto from;
}
// import [^\s,]+,
else if (c(0) == ',') {
i++;
while (i < n && ws(u(0))) i++;
// import [^\s,]+, {
if (c(0) == '{') {
i++;
// import [^\s,]+, {[^}]*
while (i < n && u(0) != '}') i++;
// import [^\s,]+, {[^}]*}
if (c(0) == '}') {
i++;
while (i < n && ws(u(0))) i++;
// import [^\s,]+, {[^}]*} from
if (bc("from", 4)) {
i += 4;
err = bare_module_lexer__add_name(env, &names, &nl, (const utf8_t *) "default", -1);
assert(err == 0);
goto from;
}
}
}
}
}
}
}
else if (bu("module", 6)) {
es = i;
i += 6;
while (i < n && ws(u(0))) i++;
// module\.
if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
// module\.exports
if (bc("exports", 7)) {
i += 7;
goto exports;
}
}
}
else if (bu("export", 6)) {
es = i;
i += 6;
// exports
if (c(0) == 's') {
i++;
goto exports;
}
while (i < n && ws(u(0))) i++;
// export \*
if (c(0) == '*') {
i++;
while (i < n && ws(u(0))) i++;
// export \* as
if (i + 3 < n && bu("as", 2) && ws(u(2))) {
i += 3;
while (i < n && ws(u(0))) i++;
// export \* as [^\s]+
while (i < n && !ws(u(0))) i++;
while (i < n && ws(u(0))) i++;
// export \* as [^\s]+ from
if (bc("from", 4)) {
type |= bare_module_lexer_import | bare_module_lexer_reexport;
is = es;
i += 4;
err = bare_module_lexer__add_name(env, &names, &nl, (const utf8_t *) "*", -1);
assert(err == 0);
goto from;
}
}
// export \* from
else if (bc("from", 4)) {
type |= bare_module_lexer_import | bare_module_lexer_reexport;
is = es;
i += 4;
err = bare_module_lexer__add_name(env, &names, &nl, (const utf8_t *) "*", -1);
assert(err == 0);
goto from;
}
}
// export {
else if (c(0) == '{') {
i++;
// export {[^}]*
while (i < n && u(0) != '}') i++;
// export {[^}]*}
if (c(0) == '}') {
i++;
while (i < n && ws(u(0))) i++;
// export {[^}]*} from
if (bc("from", 4)) {
type |= bare_module_lexer_import | bare_module_lexer_reexport;
is = es;
i += 4;
goto from;
}
}
}
}
else if (bu("__export", 8)) {
es = i;
i += 8;
// __exportStar
if (bc("Star", 4)) i += 4;
while (i < n && ws(u(0))) i++;
// __export(Star)?\(
if (c(0) == '(') {
i++;
while (i < n && ws(u(0))) i++;
// __export(Star)?\(require
if (bc("require", 7)) {
type |= bare_module_lexer_reexport;
is = i;
i += 7;
goto require;
}
}
}
else i++;
continue;
require:
type |= bare_module_lexer_require;
while (i < n && ws(u(0))) i++;
// require\.
if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
// require\.a
if (i + 5 < n && u(0) == 'a') {
i++;
// require\.addon
if (bu("ddon", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_addon;
// require\.addon\.
if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
// require\.addon\.resolve
if (i + 7 < n && bu("resolve", 7)) {
i += 7;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_resolve;
}
else continue;
}
}
// require\.asset
else if (bu("sset", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_asset;
}
}
// require\.resolve
else if (i + 7 < n && bu("resolve", 7)) {
i += 7;
while (i < n && ws(u(0))) i++;
type |= bare_module_lexer_resolve;
}
else continue;
}
// require(\.(resolve|addon(\.resolve)?|asset))?\(
if (c(0) == '(') {
i++;
while (i < n && ws(u(0))) i++;
// require(\.(resolve|addon(\.resolve)?|asset))?\(['"]
if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// require(\.(resolve|addon(\.resolve)?|asset))?\(['"].*['"]
if (c(0) == e) {
se = i;
i++;
while (i < n && ws(u(0))) i++;
if (c(0) == ',') {
i++;
while (i < n && ws(u(0))) i++;
if (c(0) == '{') {
i++;
while (i < n && ws(u(0))) i++;
if (bc("with", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
if (c(0) == ':') {
i++;
err = bare_module_lexer__lex_import_attributes(env, &attributes, s, n, i, &i);
if (err < 0) goto err;
}
}
}
}
while (i < n && u(0) != ')') i++;
// require(\.(resolve|addon(\.resolve)?|asset))?\(['"].*['"][^)]*\)
if (c(0) == ')') {
i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
}
// require\.addon(\.resolve)?\(\)
else if (c(0) == ')' && (type & bare_module_lexer_addon)) {
ss = se = i++;
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
continue;
exports:
while (i < n && ws(u(0))) i++;
// exports =
if (c(0) == '=') {
i++;
while (i < n && ws(u(0))) i++;
// exports = \{
if (c(0) == '{') {
i++;
while (c(0) != '}') {
while (i < n && ws(u(0))) i++;
if (ids(c(0))) {
ss = i++;
while (id(c(0))) i++;
se = i;
err = bare_module_lexer__add_export(env, exports, &el, s, es, ss, se);
if (err < 0) goto err;
}
while (i < n && ws(u(0))) i++;
if (c(0) != ',') break;
i++;
}
// exports = \{[^}]*\}
if (c(0) == '}') i++;
}
// exports = require
else if (bc("require", 7)) {
type |= bare_module_lexer_reexport;
is = i;
i += 7;
goto require;
}
}
// exports\.
else if (c(0) == '.') {
i++;
while (i < n && ws(u(0))) i++;
ss = i;
while (i < n && !ws(u(0)) && u(0) != '=') i++;
se = i;
while (i < n && ws(u(0))) i++;
// exports\.[^\s=] =
if (c(0) == '=') {
i++;
err = bare_module_lexer__add_export(env, exports, &el, s, es, ss, se);
if (err < 0) goto err;
}
}
// exports\[
else if (c(0) == '[') {
i++;
while (i < n && ws(u(0))) i++;
// exports\[['"]
if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// exports\[['"].*['"]
if (c(0) == e) {
se = i;
i++;
while (i < n && ws(u(0))) i++;
while (i < n && u(0) != ']') i++;
// exports\[['"].*['"][^\]]*\]
if (c(0) == ']') {
i++;
while (i < n && ws(u(0))) i++;
// exports\[['"].*['"][^\]]*\] =
if (c(0) == '=') {
i++;
err = bare_module_lexer__add_export(env, exports, &el, s, es, ss, se);
if (err < 0) goto err;
}
}
}
}
}
continue;
from:
while (i < n && ws(u(0))) i++;
// from ['"]
if (c(0) == '\'' || c(0) == '"') {
utf8_t e = u(0);
ss = ++i;
while (i < n && u(0) != e) i++;
// from ['"].*['"]
if (c(0) == e) {
se = i;
i++;
while (i < n && ws(u(0))) i++;
if (bc("with", 4)) {
i += 4;
while (i < n && ws(u(0))) i++;
err = bare_module_lexer__lex_import_attributes(env, &attributes, s, n, i, &i);
if (err < 0) goto err;
}
err = bare_module_lexer__add_import(env, imports, &il, s, is, ss, se, type, names, attributes);
if (err < 0) goto err;
}
}
continue;
}
#undef u
#undef c
#undef bu
#undef bc
#undef lt
#undef ws
#undef ids
#undef id
return 0;
err:
return -1;
}
#endif // BARE_MODULE_LEXER_H