summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rw-r--r--compiler.c85
-rw-r--r--compiler.h2
-rw-r--r--lexer.c1
-rw-r--r--lexer.h1
5 files changed, 80 insertions, 25 deletions
diff --git a/README.md b/README.md
index 55ac9c3..fc3c1da 100644
--- a/README.md
+++ b/README.md
@@ -154,9 +154,14 @@ through numeric operations, or explicitely, e.g. by invoking functions such as
Variable names must start with a letter or an underscore and may only contain
the characters `A`..`Z`, `a`..`z`, `0`..`9` or `_`. By prefixing a variable
-name with the keyword `let`, it is declared in the local function scope only
+name with the keyword `let`, it is declared in the local block scope only
and not visible outside anymore.
+Variables may also be declared using the `const` keyword. Such variables follow
+the same scoping rules as `let` declared ones but they cannot be modified after
+they have been declared. Any attempt to do so will result in a syntax error
+during compilation.
+
```javascript
{%
@@ -172,6 +177,15 @@ and not visible outside anymore.
print(a, "\n"); // outputs "2"
print(b, "\n"); // outputs nothing
+ const c = 3;
+ print(c, "\n"); // outputs "3"
+
+ c = 4; // raises syntax error
+ c++; // raises syntax error
+
+ const d; // raises syntax error, const variables must
+ // be initialized at declaration time
+
%}
```
diff --git a/compiler.c b/compiler.c
index 5a9b579..d978f38 100644
--- a/compiler.c
+++ b/compiler.c
@@ -90,7 +90,7 @@ uc_compiler_parse_rules[TK_ERROR + 1] = {
};
static ssize_t
-uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name);
+uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name, bool constant);
static ssize_t
uc_compiler_initialize_local(uc_compiler *compiler);
@@ -121,7 +121,7 @@ uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_sour
fn->strict = strict;
/* reserve stack slot 0 */
- uc_compiler_declare_local(compiler, varname);
+ uc_compiler_declare_local(compiler, varname, false);
uc_compiler_initialize_local(compiler);
ucv_put(varname);
}
@@ -640,7 +640,7 @@ uc_compiler_is_strict(uc_compiler *compiler)
}
static ssize_t
-uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name)
+uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name, bool constant)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
uc_locals *locals = &compiler->locals;
@@ -683,6 +683,7 @@ uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name)
locals->entries[locals->count].depth = -1;
locals->entries[locals->count].captured = false;
locals->entries[locals->count].from = chunk->count;
+ locals->entries[locals->count].constant = constant;
locals->count++;
return -1;
@@ -699,7 +700,7 @@ uc_compiler_initialize_local(uc_compiler *compiler)
}
static ssize_t
-uc_compiler_resolve_local(uc_compiler *compiler, uc_value_t *name)
+uc_compiler_resolve_local(uc_compiler *compiler, uc_value_t *name, bool *constant)
{
uc_locals *locals = &compiler->locals;
const char *str1, *str2;
@@ -722,6 +723,8 @@ uc_compiler_resolve_local(uc_compiler *compiler, uc_value_t *name)
return -1;
}
+ *constant = locals->entries[i - 1].constant;
+
return i - 1;
}
@@ -729,7 +732,7 @@ uc_compiler_resolve_local(uc_compiler *compiler, uc_value_t *name)
}
static ssize_t
-uc_compiler_add_upval(uc_compiler *compiler, size_t idx, bool local, uc_value_t *name)
+uc_compiler_add_upval(uc_compiler *compiler, size_t idx, bool local, uc_value_t *name, bool constant)
{
uc_function_t *function = (uc_function_t *)compiler->function;
uc_upvals *upvals = &compiler->upvals;
@@ -752,6 +755,7 @@ uc_compiler_add_upval(uc_compiler *compiler, size_t idx, bool local, uc_value_t
upvals->entries[upvals->count].local = local;
upvals->entries[upvals->count].index = idx;
upvals->entries[upvals->count].name = ucv_get(name);
+ upvals->entries[upvals->count].constant = constant;
function->nupvals++;
@@ -759,25 +763,25 @@ uc_compiler_add_upval(uc_compiler *compiler, size_t idx, bool local, uc_value_t
}
static ssize_t
-uc_compiler_resolve_upval(uc_compiler *compiler, uc_value_t *name)
+uc_compiler_resolve_upval(uc_compiler *compiler, uc_value_t *name, bool *constant)
{
ssize_t idx;
if (!compiler->parent)
return -1;
- idx = uc_compiler_resolve_local(compiler->parent, name);
+ idx = uc_compiler_resolve_local(compiler->parent, name, constant);
if (idx > -1) {
compiler->parent->locals.entries[idx].captured = true;
- return uc_compiler_add_upval(compiler, idx, true, name);
+ return uc_compiler_add_upval(compiler, idx, true, name, *constant);
}
- idx = uc_compiler_resolve_upval(compiler->parent, name);
+ idx = uc_compiler_resolve_upval(compiler->parent, name, constant);
if (idx > -1)
- return uc_compiler_add_upval(compiler, idx, false, name);
+ return uc_compiler_add_upval(compiler, idx, false, name, *constant);
return -1;
}
@@ -829,6 +833,7 @@ static void
uc_compiler_emit_inc_dec(uc_compiler *compiler, uc_tokentype_t toktype, bool is_postfix)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
+ uc_value_t *varname = NULL;
enum insn_type type;
uint32_t cidx = 0;
@@ -838,6 +843,16 @@ uc_compiler_emit_inc_dec(uc_compiler *compiler, uc_tokentype_t toktype, bool is_
if (type == I_LVAR || type == I_LLOC || type == I_LUPV) {
cidx = uc_compiler_get_u32(compiler, compiler->last_insn + 1);
+ if (type == I_LLOC && compiler->locals.entries[cidx].constant)
+ varname = compiler->locals.entries[cidx].name;
+ else if (type == I_LUPV && compiler->upvals.entries[cidx].constant)
+ varname = compiler->upvals.entries[cidx].name;
+
+ if (varname)
+ uc_compiler_syntax_error(compiler, 0,
+ "Invalid increment/decrement of constant '%s'",
+ ucv_string_get(varname));
+
uc_chunk_pop(chunk);
uc_chunk_pop(chunk);
uc_chunk_pop(chunk);
@@ -1027,6 +1042,7 @@ uc_compiler_emit_variable_rw(uc_compiler *compiler, uc_value_t *varname, uc_toke
{
enum insn_type insn;
uint32_t sub_insn;
+ bool constant;
ssize_t idx;
switch (type) {
@@ -1051,16 +1067,24 @@ uc_compiler_emit_variable_rw(uc_compiler *compiler, uc_value_t *varname, uc_toke
if (sub_insn)
uc_compiler_emit_u8(compiler, compiler->parser->prev.pos, sub_insn);
}
- else if ((idx = uc_compiler_resolve_local(compiler, varname)) > -1) {
+ else if ((idx = uc_compiler_resolve_local(compiler, varname, &constant)) > -1) {
insn = sub_insn ? I_ULOC : (type ? I_SLOC : I_LLOC);
+ if (insn != I_LLOC && constant)
+ uc_compiler_syntax_error(compiler, 0,
+ "Invalid assignment to constant '%s'", ucv_string_get(varname));
+
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, insn);
uc_compiler_emit_u32(compiler, compiler->parser->prev.pos,
((sub_insn & 0xff) << 24) | idx);
}
- else if ((idx = uc_compiler_resolve_upval(compiler, varname)) > -1) {
+ else if ((idx = uc_compiler_resolve_upval(compiler, varname, &constant)) > -1) {
insn = sub_insn ? I_UUPV : (type ? I_SUPV : I_LUPV);
+ if (insn != I_LUPV && constant)
+ uc_compiler_syntax_error(compiler, 0,
+ "Invalid assignment to constant '%s'", ucv_string_get(varname));
+
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, insn);
uc_compiler_emit_u32(compiler, compiler->parser->prev.pos,
((sub_insn & 0xff) << 24) | idx);
@@ -1130,7 +1154,7 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, uc_value_t *args, bool restar
/* declare local variables for arguments */
for (i = 0; i < fn->nargs; i++) {
slot = uc_compiler_declare_local(&fncompiler,
- array ? ucv_array_get(args, i) : args);
+ array ? ucv_array_get(args, i) : args, false);
if (slot != -1)
uc_compiler_syntax_error(&fncompiler, pos,
@@ -1523,7 +1547,7 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable)
/* Named functions are syntactic sugar for local variable declaration
* with function value assignment. If a name token was encountered,
* initialize a local variable for it... */
- slot = uc_compiler_declare_local(compiler, name);
+ slot = uc_compiler_declare_local(compiler, name, false);
if (slot == -1)
uc_compiler_initialize_local(compiler);
@@ -1553,7 +1577,7 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable)
if (uc_compiler_parse_match(&fncompiler, TK_LABEL)) {
fn->nargs++;
- uc_compiler_declare_local(&fncompiler, fncompiler.parser->prev.uv);
+ uc_compiler_declare_local(&fncompiler, fncompiler.parser->prev.uv, false);
uc_compiler_initialize_local(&fncompiler);
if (fn->vararg ||
@@ -1863,7 +1887,7 @@ uc_compiler_compile_object(uc_compiler *compiler, bool assignable)
static void
uc_compiler_declare_local_null(uc_compiler *compiler, size_t srcpos, uc_value_t *varname)
{
- ssize_t existing_slot = uc_compiler_declare_local(compiler, varname);
+ ssize_t existing_slot = uc_compiler_declare_local(compiler, varname, false);
uc_compiler_emit_insn(compiler, srcpos, I_LNULL);
@@ -1888,7 +1912,7 @@ uc_compiler_declare_internal(uc_compiler *compiler, size_t srcpos, const char *n
n = xjs_new_string(name);
strict = compiler->strict_declarations;
compiler->strict_declarations = false;
- existing_slot = uc_compiler_declare_local(compiler, n);
+ existing_slot = uc_compiler_declare_local(compiler, n, false);
compiler->strict_declarations = strict;
uc_compiler_emit_insn(compiler, srcpos, I_LNULL);
@@ -1925,7 +1949,7 @@ uc_compiler_declare_internal(uc_compiler *compiler, size_t srcpos, const char *n
}
static void
-uc_compiler_compile_declexpr(uc_compiler *compiler)
+uc_compiler_compile_declexpr(uc_compiler *compiler, bool constant)
{
ssize_t slot;
@@ -1934,14 +1958,18 @@ uc_compiler_compile_declexpr(uc_compiler *compiler)
uc_compiler_parse_consume(compiler, TK_LABEL);
/* declare local variable */
- slot = uc_compiler_declare_local(compiler, compiler->parser->prev.uv);
+ slot = uc_compiler_declare_local(compiler, compiler->parser->prev.uv, constant);
/* if followed by '=', parse initializer expression */
if (uc_compiler_parse_match(compiler, TK_ASSIGN))
uc_compiler_parse_precedence(compiler, P_ASSIGN);
- /* otherwise load implicit null */
- else
+ /* otherwise, for writable variables, load implicit null */
+ else if (!constant)
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_LNULL);
+ /* for constant variables, a missing initializer is a syntax error */
+ else
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Expecting initializer expression");
/* initialize local */
if (slot == -1) {
@@ -1960,7 +1988,14 @@ uc_compiler_compile_declexpr(uc_compiler *compiler)
static void
uc_compiler_compile_local(uc_compiler *compiler)
{
- uc_compiler_compile_declexpr(compiler);
+ uc_compiler_compile_declexpr(compiler, false);
+ uc_compiler_parse_consume(compiler, TK_SCOL);
+}
+
+static void
+uc_compiler_compile_const(uc_compiler *compiler)
+{
+ uc_compiler_compile_declexpr(compiler, true);
uc_compiler_parse_consume(compiler, TK_SCOL);
}
@@ -2281,7 +2316,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var)
if (uc_compiler_parse_match(compiler, TK_COMMA)) {
/* Is a continuation of a declaration list... */
if (local) {
- uc_compiler_compile_declexpr(compiler);
+ uc_compiler_compile_declexpr(compiler, false);
}
/* ... otherwise an unrelated expression */
else {
@@ -2631,7 +2666,7 @@ uc_compiler_compile_try(uc_compiler *compiler)
if (uc_compiler_parse_match(compiler, TK_LPAREN)) {
uc_compiler_parse_consume(compiler, TK_LABEL);
- uc_compiler_declare_local(compiler, compiler->parser->prev.uv);
+ uc_compiler_declare_local(compiler, compiler->parser->prev.uv, false);
uc_compiler_initialize_local(compiler);
uc_compiler_parse_consume(compiler, TK_RPAREN);
@@ -2806,6 +2841,8 @@ uc_compiler_compile_declaration(uc_compiler *compiler)
{
if (uc_compiler_parse_match(compiler, TK_LOCAL))
uc_compiler_compile_local(compiler);
+ else if (uc_compiler_parse_match(compiler, TK_CONST))
+ uc_compiler_compile_const(compiler);
else
uc_compiler_compile_statement(compiler);
diff --git a/compiler.h b/compiler.h
index deec3d5..54f56c2 100644
--- a/compiler.h
+++ b/compiler.h
@@ -76,12 +76,14 @@ typedef struct {
ssize_t depth;
size_t from;
bool captured;
+ bool constant;
} uc_local;
typedef struct {
uc_value_t *name;
size_t index;
bool local;
+ bool constant;
} uc_upval;
uc_declare_vector(uc_locals, uc_local);
diff --git a/lexer.c b/lexer.c
index cc35d66..95acfe6 100644
--- a/lexer.c
+++ b/lexer.c
@@ -152,6 +152,7 @@ static const struct keyword reserved_words[] = {
{ TK_WHILE, "while", 5, { 0 } },
{ TK_BREAK, "break", 5, { 0 } },
{ TK_CATCH, "catch", 5, { 0 } },
+ { TK_CONST, "const", 5, { 0 } },
{ TK_BOOL, "false", 5, { .b = false } },
{ TK_BOOL, "true", 4, { .b = true } },
{ TK_ELIF, "elif", 4, { 0 } },
diff --git a/lexer.h b/lexer.h
index e31f78a..96cdff8 100644
--- a/lexer.h
+++ b/lexer.h
@@ -104,6 +104,7 @@ typedef enum {
TK_NULL,
TK_THIS,
TK_DELETE,
+ TK_CONST,
TK_EOF,
TK_ERROR