enhanced the compiler logic to handle the class-level variales
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							
								
								
									
										220
									
								
								lib/comp.c
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								lib/comp.c
									
									
									
									
									
								
							| @ -33,6 +33,9 @@ | ||||
| #define FOR_TRY   (2) | ||||
| #define FOR_CLASS (3) | ||||
|  | ||||
| #define MAX_NIVARS (HCL_SMOOI_MAX < MAX_CODE_PARAM2? HCL_SMOOI_MAX: MAX_CODE_PARAM2) | ||||
| #define MAX_NCVARS (HCL_SMOOI_MAX < MAX_CODE_PARAM2? HCL_SMOOI_MAX: MAX_CODE_PARAM2) | ||||
|  | ||||
| enum | ||||
| { | ||||
| 	VAR_NAMED, | ||||
| @ -1475,7 +1478,9 @@ static int collect_vardcl_for_class (hcl_t* hcl, hcl_cnode_t* obj, hcl_cnode_t** | ||||
| 			if (enclosed) | ||||
| 			{ | ||||
| 			synerr_varname: | ||||
| 				hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(var), HCL_CNODE_GET_TOK(var), "not variable name"); | ||||
| 				hcl_setsynerrbfmt ( | ||||
| 					hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(var), HCL_NULL, | ||||
| 					"not variable name '%.*js'", HCL_CNODE_GET_TOKLEN(var), HCL_CNODE_GET_TOKPTR(var)); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			enclosed = 1; | ||||
| @ -1499,6 +1504,14 @@ static int collect_vardcl_for_class (hcl_t* hcl, hcl_cnode_t* obj, hcl_cnode_t** | ||||
| 		if (enclosed) | ||||
| 		{ | ||||
| 			/* class variable */ | ||||
| 			if (vardcl->nivars >= MAX_NCVARS) | ||||
| 			{ | ||||
| 				hcl_setsynerrbfmt ( | ||||
| 					hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(var), HCL_NULL, | ||||
| 					"too many(%zu) class variables before '%.*js'", | ||||
| 					vardcl->nivars, HCL_CNODE_GET_TOKLEN(var), HCL_CNODE_GET_TOKPTR(var)); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			/*if (cvar_len <= 0) cvar_start = prev_tv_len; | ||||
| 			cvar_len = hcl->c->tv.s.len - cvar_start; */ | ||||
| 			if (vardcl->cvar_len <= 0) vardcl->cvar_start = checkpoint; | ||||
| @ -1508,6 +1521,14 @@ static int collect_vardcl_for_class (hcl_t* hcl, hcl_cnode_t* obj, hcl_cnode_t** | ||||
| 		else | ||||
| 		{ | ||||
| 			/* instance variable */ | ||||
| 			if (vardcl->nivars >= MAX_NIVARS) | ||||
| 			{ | ||||
| 				hcl_setsynerrbfmt ( | ||||
| 					hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(var), HCL_NULL, | ||||
| 					"too many(%zu) instance variables before '%.*js'", | ||||
| 					vardcl->nivars, HCL_CNODE_GET_TOKLEN(var), HCL_CNODE_GET_TOKPTR(var)); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			if (vardcl->ivar_len <= 0) vardcl->ivar_start = (vardcl->cvar_len <= 0)? checkpoint: vardcl->cvar_start; | ||||
| 			vardcl->ivar_len += hcl->c->tv.s.len - checkpoint; | ||||
| 			if (vardcl->cvar_len > 0) | ||||
| @ -2908,55 +2929,9 @@ static HCL_INLINE int compile_class_p1 (hcl_t* hcl) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (vardcl.nivars > 0) | ||||
| 	{ | ||||
| 		hcl_oop_t tmp; | ||||
| 		int adj; | ||||
|  | ||||
| 		if (vardcl.nivars > HCL_SMOOI_MAX) | ||||
| 		{ | ||||
| 			hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(cf->operand), HCL_NULL, "too many(%zu) instance variables", vardcl.nivars); | ||||
| 			goto oops; | ||||
| 		} | ||||
|  | ||||
| 		/* set starting point past the added space (+1 to index, -1 to length) */ | ||||
| 		adj = (hcl->c->tv.s.ptr[vardcl.ivar_start] == ' '); | ||||
| 		tmp = hcl_makestring(hcl, &hcl->c->tv.s.ptr[vardcl.ivar_start + adj], vardcl.ivar_len - adj); | ||||
| 		if (HCL_UNLIKELY(!tmp)) goto oops; | ||||
| 		if (emit_push_literal(hcl, tmp, &cf->u._class.start_loc) <= -1) goto oops; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* TODO: remove the if part */ | ||||
| 		/* emit a placeholder instruction to be patched in compile_class_p2() */ | ||||
| 		if (emit_single_param_instruction(hcl, HCL_CODE_PUSH_LITERAL_0, MAX_CODE_PARAM2, &cf->u._class.start_loc) <= -1) return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (vardcl.ncvars > 0) | ||||
| 	{ | ||||
| 		hcl_oop_t tmp; | ||||
| 		int adj; | ||||
|  | ||||
| 		if (vardcl.ncvars > HCL_SMOOI_MAX) | ||||
| 		{ | ||||
| 			/* TOOD: change the error location ?? */ | ||||
| 			hcl_setsynerrbfmt ( | ||||
| 				hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(cf->operand), HCL_NULL, | ||||
| 				"too many(%zu) class variables", vardcl.ncvars); | ||||
| 			goto oops; | ||||
| 		} | ||||
|  | ||||
| 		adj = (hcl->c->tv.s.ptr[vardcl.cvar_start] == ' '); | ||||
| 		tmp = hcl_makestring(hcl, &hcl->c->tv.s.ptr[vardcl.cvar_start + adj], vardcl.cvar_len - adj); | ||||
| 		if (HCL_UNLIKELY(!tmp)) goto oops; | ||||
| 		if (emit_push_literal(hcl, tmp, &cf->u._class.start_loc) <= -1) goto oops; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* TODO: remove the if part */ | ||||
| 		/* emit a placeholder instruction to be patched in compile_class_p2() */ | ||||
| 		if (emit_single_param_instruction(hcl, HCL_CODE_PUSH_LITERAL_0, MAX_CODE_PARAM2, &cf->u._class.start_loc) <= -1) return -1; | ||||
| 	} | ||||
| 	/* emit placeholder instructions to be patched in compile_class_p2() */ | ||||
| 	if (emit_single_param_instruction(hcl, HCL_CODE_PUSH_LITERAL_0, MAX_CODE_PARAM2, &cf->u._class.start_loc) <= -1) return -1; | ||||
| 	if (emit_single_param_instruction(hcl, HCL_CODE_PUSH_LITERAL_0, MAX_CODE_PARAM2, &cf->u._class.start_loc) <= -1) return -1; | ||||
|  | ||||
| 	if (check_block_expression_as_body(hcl, obj, cf->u._class.cmd_cnode, FOR_CLASS) <= -1) return -1; | ||||
|  | ||||
| @ -3002,6 +2977,7 @@ static HCL_INLINE int compile_class_p2 (hcl_t* hcl) | ||||
| 	hcl_cnode_t* class_name; | ||||
| 	hcl_loc_t class_loc; | ||||
| 	hcl_clsblk_info_t* cbi; | ||||
| 	hcl_oow_t patch_pos, patch_end; | ||||
|  | ||||
| 	cf = GET_TOP_CFRAME(hcl); | ||||
| 	HCL_ASSERT (hcl, cf->opcode == COP_COMPILE_CLASS_P2); | ||||
| @ -3017,60 +2993,64 @@ static HCL_INLINE int compile_class_p2 (hcl_t* hcl) | ||||
| 	} | ||||
|  | ||||
| 	cbi = &hcl->c->clsblk.info[hcl->c->clsblk.depth]; | ||||
| 	if (cbi->class_enter_inst_pos) | ||||
| 	/* patch the CLASS_ENTER instruction with the final nicvars and ncvars values. | ||||
| 	 * CLASS_ENTER nsuperclasses(lp)|nivars(lp)|ncvars(lp)|spec/selfspec(b)|index_type(b) | ||||
| 	 *  (lp) = long param, (b) = byte */ | ||||
| 	patch_pos = cbi->class_enter_inst_pos + 1; | ||||
| 	patch_pos += HCL_CODE_LONG_PARAM_SIZE; /* skip nsuperclasses */ | ||||
| 	patch_long_param (hcl, patch_pos, cbi->nivars); | ||||
| 	patch_pos += HCL_CODE_LONG_PARAM_SIZE; /* skip nivars */ | ||||
| 	patch_long_param (hcl, patch_pos, cbi->ncvars); | ||||
|  | ||||
| 	/* two placeholder instructions have been pushed before push_clsblk() | ||||
| 	 * in compile_class_p1(). | ||||
| 	 *   push_literal long-param long-param <-- (1) position of first long-param | ||||
| 	 *   push_literal long-param long-param <-- (2) position of first long-param | ||||
| 	 *   class_enter ...                    <-- class_enter_inst_pos | ||||
| 	 */ | ||||
| 	patch_pos = cbi->class_enter_inst_pos - (HCL_CODE_LONG_PARAM_SIZE * 4 + 1); /* (1) */ | ||||
| 	if (cbi->nivars > 0) | ||||
| 	{ | ||||
| 		/* patch the CLASS_ENTER instruction with the final nicvars and ncvars values. | ||||
| 		 * CLASS_ENTER nsuperclasses(lp)|nivars(lp)|ncvars(lp)|spec/selfspec(b)|index_type(b) | ||||
| 		 *  (lp) = long param, (b) = byte */ | ||||
| 		hcl_oow_t patch_pos, patch_end; | ||||
| 		/* patch the PUSH_LITERAL instruction for ivars */ | ||||
| 		/* TODO: reduce space waste for fixed double-long param */ | ||||
| 		hcl_oop_t obj; | ||||
| 		hcl_oow_t index; | ||||
|  | ||||
| 		patch_pos = cbi->class_enter_inst_pos + 1; | ||||
| 		patch_pos += HCL_CODE_LONG_PARAM_SIZE; /* skip nsuperclasses */ | ||||
| 		patch_long_param (hcl, patch_pos, cbi->nivars); | ||||
| 		patch_pos += HCL_CODE_LONG_PARAM_SIZE; /* skip nivars */ | ||||
| 		patch_long_param (hcl, patch_pos, cbi->ncvars); | ||||
| 		HCL_ASSERT (hcl, cbi->nivars <= MAX_NIVARS); | ||||
| 		obj = hcl_makestring(hcl, cbi->ivars.ptr, cbi->ivars.len); | ||||
| 		if (HCL_UNLIKELY(!obj)) return -1; | ||||
| 		if (add_literal(hcl, obj, &index) <= -1) return -1; | ||||
| 		patch_double_long_params_with_oow(hcl, patch_pos, index); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* TODO: reduce space waste for patched NOOP */ | ||||
| 		patch_end = (--patch_pos) + (HCL_CODE_LONG_PARAM_SIZE * 2) + 1; | ||||
| 		for (; patch_pos < patch_end; patch_pos++) | ||||
| 			patch_instruction (hcl, patch_pos, HCL_CODE_NOOP); | ||||
| 	} | ||||
|  | ||||
| 		patch_pos = cbi->class_enter_inst_pos - (HCL_CODE_LONG_PARAM_SIZE * 4 + 1); | ||||
| 		if (cbi->nivars > 0) | ||||
| 		{ | ||||
| 			/* patch the PUSH_LITERAL instruction for ivars */ | ||||
| 			/* TODO: reduce space waste for fixed double-long param */ | ||||
| 			hcl_oop_t obj; | ||||
| 			hcl_oow_t index; | ||||
| 	patch_pos = cbi->class_enter_inst_pos - (HCL_CODE_LONG_PARAM_SIZE * 2); /* (2) */ | ||||
| 	if (cbi->ncvars > 0) | ||||
| 	{ | ||||
| 		/* patch the PUSH_LITERAL instruction for cvars */ | ||||
| 		/* TODO: reduce space waste for fixed double-long param */ | ||||
| 		hcl_oop_t obj; | ||||
| 		hcl_oow_t index; | ||||
|  | ||||
| 			obj = hcl_makestring(hcl, cbi->ivars.ptr, cbi->ivars.len); | ||||
| 			if (HCL_UNLIKELY(!obj)) return -1; | ||||
| 			if (add_literal(hcl, obj, &index) <= -1) return -1; | ||||
| 			patch_double_long_params_with_oow(hcl, patch_pos, index); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* TODO: reduce space waste for patched NOOP */ | ||||
| 			patch_end = (--patch_pos) + (HCL_CODE_LONG_PARAM_SIZE * 2) + 1; | ||||
| 			for (; patch_pos < patch_end; patch_pos++) | ||||
| 				patch_instruction (hcl, patch_pos, HCL_CODE_NOOP); | ||||
| 		} | ||||
|  | ||||
| 		patch_pos = cbi->class_enter_inst_pos - (HCL_CODE_LONG_PARAM_SIZE * 2); | ||||
| 		if (cbi->ncvars > 0) | ||||
| 		{ | ||||
| 			/* patch the PUSH_LITERAL instruction for cvars */ | ||||
| 			/* TODO: reduce space waste for fixed double-long param */ | ||||
| 			hcl_oop_t obj; | ||||
| 			hcl_oow_t index; | ||||
| 			obj = hcl_makestring(hcl, cbi->cvars.ptr, cbi->cvars.len); | ||||
| 			if (HCL_UNLIKELY(!obj)) return -1; | ||||
| 			if (add_literal(hcl, obj, &index) <= -1) return -1; | ||||
| 			patch_double_long_params_with_oow(hcl, patch_pos, index); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* TODO: reduce space waste for patched NOOP */ | ||||
| 			patch_end = (--patch_pos) + (HCL_CODE_LONG_PARAM_SIZE * 2) + 1; | ||||
| 			HCL_ASSERT (hcl, patch_end == cbi->class_enter_inst_pos); | ||||
| 			for (;patch_pos < patch_end; patch_pos++) | ||||
| 				patch_instruction (hcl, patch_pos, HCL_CODE_NOOP); | ||||
| 		} | ||||
| 		HCL_ASSERT (hcl, cbi->ncvars <= MAX_NCVARS); | ||||
| 		obj = hcl_makestring(hcl, cbi->cvars.ptr, cbi->cvars.len); | ||||
| 		if (HCL_UNLIKELY(!obj)) return -1; | ||||
| 		if (add_literal(hcl, obj, &index) <= -1) return -1; | ||||
| 		patch_double_long_params_with_oow(hcl, patch_pos, index); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* TODO: reduce space waste for patched NOOP */ | ||||
| 		patch_end = (--patch_pos) + (HCL_CODE_LONG_PARAM_SIZE * 2) + 1; | ||||
| 		HCL_ASSERT (hcl, patch_end == cbi->class_enter_inst_pos); | ||||
| 		for (;patch_pos < patch_end; patch_pos++) | ||||
| 			patch_instruction (hcl, patch_pos, HCL_CODE_NOOP); | ||||
| 	} | ||||
|  | ||||
| 	pop_ctlblk (hcl); | ||||
| @ -3747,8 +3727,8 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 	hcl_cnode_t* attr_list; | ||||
| 	hcl_clsblk_info_t* cbi; | ||||
|  | ||||
| 	/* this is for install/class variable declaration. | ||||
| 	 * and generates no instruction */ | ||||
| 	/* this is for instance/class variable declaration | ||||
| 	 * inside the class body block */ | ||||
|  | ||||
| 	cmd = HCL_CNODE_CONS_CAR(src); | ||||
| 	next = HCL_CNODE_CONS_CDR(src); | ||||
| @ -3765,7 +3745,7 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 			hcl, HCL_SYNERR_VAR, HCL_CNODE_GET_LOC(cmd), HCL_NULL, | ||||
| 			"'%.*js' not followed by name or (", | ||||
| 			HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); | ||||
| 		return -1; | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	/* the reader ensures that the cdr field of a cons cell points to the next cell. | ||||
| @ -3778,7 +3758,7 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 			hcl, HCL_SYNERR_BANNED, HCL_CNODE_GET_LOC(cmd), HCL_NULL, | ||||
| 			"'%.*js' prohibited in this context", | ||||
| 			HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); | ||||
| 		return -1; | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	tmp = HCL_CNODE_CONS_CAR(next); | ||||
| @ -3795,7 +3775,7 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 				hcl, HCL_SYNERR_VAR, HCL_CNODE_GET_LOC(attr_list), HCL_NULL, | ||||
| 				"no name after attribute list for '%.*js'", | ||||
| 				HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); | ||||
| 			return -1; | ||||
| 			goto oops; | ||||
| 		} | ||||
|  | ||||
| 		tmp = HCL_CNODE_CONS_CAR(next); | ||||
| @ -3805,19 +3785,37 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 	{ | ||||
| 		unsigned int var_type = VAR_INST; | ||||
|  | ||||
| 		if (attr_list && check_var_attr_list(hcl, attr_list, &var_type, cmd, cbi->class_name, tmp) <= -1) return -1; | ||||
| 		if (attr_list && check_var_attr_list(hcl, attr_list, &var_type, cmd, cbi->class_name, tmp) <= -1) goto oops; | ||||
|  | ||||
| 		HCL_ASSERT (hcl, var_type == VAR_INST || var_type == VAR_CLASS_I); | ||||
| 		while (1) | ||||
| 		{ | ||||
| 			if (var_type == VAR_INST) | ||||
| 			{ | ||||
| 				if (add_class_level_variable(hcl, &cbi->ivars, tmp, "instance") <= -1) return -1; | ||||
| 				if (cbi->nivars >= MAX_NIVARS) | ||||
| 				{ | ||||
| 					hcl_setsynerrbfmt ( | ||||
| 						hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(tmp), HCL_NULL, | ||||
| 						"too many(%zu) instance variables before '%.*js'", | ||||
| 						cbi->nivars, HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); | ||||
| 					goto oops; | ||||
| 				} | ||||
|  | ||||
| 				if (add_class_level_variable(hcl, &cbi->ivars, tmp, "instance") <= -1) goto oops; | ||||
| 				cbi->nivars++; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (add_class_level_variable(hcl, &cbi->cvars, tmp, "class") <= -1) return -1; | ||||
| 				if (cbi->ncvars >= MAX_NCVARS) | ||||
| 				{ | ||||
| 					hcl_setsynerrbfmt ( | ||||
| 						hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(tmp), HCL_NULL, | ||||
| 						"too many(%zu) class variables before '%.*js'", | ||||
| 						cbi->ncvars, HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); | ||||
| 					goto oops; | ||||
| 				} | ||||
|  | ||||
| 				if (add_class_level_variable(hcl, &cbi->cvars, tmp, "class") <= -1) goto oops; | ||||
| 				cbi->ncvars++; | ||||
| 			} | ||||
|  | ||||
| @ -3835,7 +3833,7 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
| 			"invalid variable name '%.*js' for '%.*js'", | ||||
| 			HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp), | ||||
| 			HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); | ||||
| 		return -1; | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| /* if there is assignment with expression, we can arragne to compile RHS. in that case | ||||
| @ -3846,6 +3844,10 @@ static int compile_var (hcl_t* hcl, hcl_cnode_t* src) | ||||
|  | ||||
| 	POP_CFRAME (hcl); | ||||
| 	return 0; | ||||
|  | ||||
|  | ||||
| oops: | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static int compile_return (hcl_t* hcl, hcl_cnode_t* src, int ret_from_home) | ||||
|  | ||||
| @ -281,3 +281,20 @@ class(#byte #limited #final #limited) Kuduro { ##ERROR: syntax error - conflicti | ||||
| --- | ||||
| class(#byte #bytes) Kuduro { ##ERROR: syntax error - unrecognized class attribute name '#bytes' | ||||
| } | ||||
|  | ||||
| --- | ||||
| class Kuduro [a b c] { | ||||
| 	var d e | ||||
| 	var a  ##ERROR: syntax error - duplicate instance variable name 'a' | ||||
| } | ||||
|  | ||||
| --- | ||||
| class Kuduru [a [b] c] { | ||||
| 	var d e | ||||
| 	var b    ##TODO: should this be prohibited? | ||||
| } | ||||
|  | ||||
| class Kuduro [a [b] c] { | ||||
| 	var d e | ||||
| 	var(#class) b ##ERROR: syntax error - duplicate class variable name 'b' | ||||
| } | ||||
|  | ||||
| @ -31,7 +31,7 @@ fun String length() { ##ERROR: syntax error - 'String' not followed by ( but fol | ||||
|  | ||||
| --- | ||||
|  | ||||
| class A [ 10 ] { ##ERROR: syntax error - not variable name - 10 | ||||
| class A [ 10 ] { ##ERROR: syntax error - not variable name '10' | ||||
| } | ||||
|  | ||||
| --- | ||||
| @ -44,11 +44,11 @@ class A [ [ [a] ] ] { ##ERROR: syntax error - not variable name | ||||
| } | ||||
|  | ||||
| --- | ||||
| class A [ a + ] { ##ERROR: syntax error - not variable name - + | ||||
| class A [ a + ] { ##ERROR: syntax error - not variable name '+' | ||||
| } | ||||
|  | ||||
| --- | ||||
| class A [ + ] { ##ERROR: syntax error - not variable name - + | ||||
| class A [ + ] { ##ERROR: syntax error - not variable name '+' | ||||
| } | ||||
|  | ||||
| --- | ||||
|  | ||||
		Reference in New Issue
	
	Block a user