예제 #1
0
ExprPtr Parser::patternMatch(bool se)
{
	expect(TCase);
	auto const& value = primaryExpression(false);
	expect(TLBrace);

	IntrusiveRefCntPtr<MatchExpr> matchExpr(new MatchExpr(value));

	do
	{
		if(token == TTypeIdent || token == TIdent) // TODO: firstPattern set
		{
			enterScope(new Scope);

			auto pt = pattern(false);
			
			// TODO: Allow pattern = expr
			auto body = exitScope<Expr>(block(true));

			matchExpr->legs.push_back(MatchLeg(pt, body));
		}
	}
	while(test(TSemicolon));

	expect(TRBrace, se);

	return ExprPtr(matchExpr.getPtr()); // TEMP
}
예제 #2
0
파일: php.c 프로젝트: blackb1rd/ctags
/* parses a class or an interface:
 * 	class Foo {}
 * 	class Foo extends Bar {}
 * 	class Foo extends Bar implements iFoo, iBar {}
 * 	interface iFoo {}
 * 	interface iBar extends iFoo {}
 *
 * if @name is not NULL, parses an anonymous class with name @name
 * 	new class {}
 * 	new class(1, 2) {}
 * 	new class(1, 2) extends Foo implements iFoo, iBar {} */
static boolean parseClassOrIface (tokenInfo *const token, const phpKind kind,
                                  const tokenInfo *name)
{
	boolean readNext = TRUE;
	implType impl = CurrentStatement.impl;
	tokenInfo *nameFree = NULL;
	vString *inheritance = NULL;

	readToken (token);
	if (name) /* anonymous class */
	{
		/* skip possible construction arguments */
		skipOverParens (token);
	}
	else /* normal, named class */
	{
		if (token->type != TOKEN_IDENTIFIER)
			return FALSE;

		name = nameFree = newToken ();
		copyToken (nameFree, token, TRUE);

		readToken (token);
	}

	inheritance = vStringNew ();
	/* read every identifiers, keywords and commas, and assume each
	 *  identifier (not keyword) is an inheritance
	 * (like in "class Foo extends Bar implements iA, iB") */
	while (token->type == TOKEN_IDENTIFIER ||
	       token->type == TOKEN_KEYWORD ||
	       token->type == TOKEN_COMMA)
	{
		if (token->type == TOKEN_IDENTIFIER)
		{
			if (vStringLength (inheritance) > 0)
				vStringPut (inheritance, ',');
			vStringCat (inheritance, token->string);
		}

		readToken (token);
	}

	makeClassOrIfaceTag (kind, name, inheritance, impl);

	if (token->type == TOKEN_OPEN_CURLY)
		enterScope (token, name->string, kind);
	else
		readNext = FALSE;

	if (nameFree)
		deleteToken (nameFree);
	vStringDelete (inheritance);

	return readNext;
}
예제 #3
0
static void findPowerShellTags (void)
{
	tokenInfo *const token = newToken ();

	do
	{
		enterScope (token, NULL, -1);
	}
	while (token->type != TOKEN_EOF); /* keep going even with unmatched braces */

	deleteToken (token);
}
예제 #4
0
ExprPtr Parser::controlSeqExpression(bool se)
{
	if(test(TIf))
	{
		auto ifExpr = enterScope<IfExpr>(new IfExpr);
		ifExpr->cond = expression(false);
		enterScope(new Scope);
		ifExpr->trueCase = exitScope<Expr>(controlSeqExpression(se));
		exitScope<IfExpr>(ExprPtr());
		ExprPtr ret = ifExpr;
		if(test(TElse))
		{
			enterScope(new Scope);
			auto falseCase = exitScope<Expr>(controlSeqExpression(se));
			ret = inScope(new IfElseExpr(ifExpr, falseCase));
		}
		return ret;
	}
	/* TODO
	else if (Test(Token.While))
	{
		var whileExpr = EnterScopeT<WhileExpr>();

		whileExpr.Cond = Expression(false);
		whileExpr.Body = ControlSeqExpression(se);
		ExitScopeT<WhileExpr>(null);
		return whileExpr;
	}*/
	else if(token == TLBrace)
	{
		return block(se);
	}

	assert(false);
	return ExprPtr();
}
예제 #5
0
static void enterScope (tokenInfo *const parentToken,
						const vString *const extraScope,
						const int parentKind)
{
	tokenInfo *token = newToken ();
	int origParentKind = parentToken->parentKind;

	copyToken (token, parentToken, true);

	if (extraScope)
	{
		addToScope (token, extraScope);
		token->parentKind = parentKind;
	}

	readToken (token);
	while (token->type != TOKEN_EOF &&
		   token->type != TOKEN_CLOSE_CURLY)
	{
		bool readNext = true;

		switch (token->type)
		{
			case TOKEN_OPEN_CURLY:
				enterScope (token, NULL, -1);
				break;

			case TOKEN_KEYWORD:
				readNext = parseFunction (token);
				break;

			case TOKEN_VARIABLE:
				readNext = parseVariable (token);
				break;

			default: break;
		}

		if (readNext)
			readToken (token);
	}

	copyToken (parentToken, token, false);
	parentToken->parentKind = origParentKind;
	deleteToken (token);
}
예제 #6
0
파일: php.c 프로젝트: blackb1rd/ctags
static void findTags (boolean startsInPhpMode)
{
	tokenInfo *const token = newToken ();

	InPhp = startsInPhpMode;
	CurrentStatement.access = ACCESS_UNDEFINED;
	CurrentStatement.impl = IMPL_UNDEFINED;
	CurrentNamesapce = vStringNew ();
	FullScope = vStringNew ();
	AnonymousID = 0;

	do
	{
		enterScope (token, NULL, -1);
	}
	while (token->type != TOKEN_EOF); /* keep going even with unmatched braces */

	vStringDelete (FullScope);
	vStringDelete (CurrentNamesapce);
	deleteToken (token);
}
예제 #7
0
파일: php.c 프로젝트: blackb1rd/ctags
/* parses namespace declarations
 * 	namespace Foo {}
 * 	namespace Foo\Bar {}
 * 	namespace Foo;
 * 	namespace Foo\Bar;
 * 	namespace;
 * 	napespace {} */
static boolean parseNamespace (tokenInfo *const token)
{
	tokenInfo *nsToken = newToken ();

	vStringClear (CurrentNamesapce);
	copyToken (nsToken, token, FALSE);

	do
	{
		readToken (token);
		if (token->type == TOKEN_IDENTIFIER)
		{
			if (vStringLength (CurrentNamesapce) > 0)
			{
				const char *sep;

				sep = phpScopeSeparatorFor(K_NAMESPACE,
							   K_NAMESPACE);
				vStringCatS (CurrentNamesapce, sep);
			}
			vStringCat (CurrentNamesapce, token->string);
		}
	}
	while (token->type != TOKEN_EOF &&
		   token->type != TOKEN_SEMICOLON &&
		   token->type != TOKEN_OPEN_CURLY);

	vStringTerminate (CurrentNamesapce);
	if (vStringLength (CurrentNamesapce) > 0)
		makeNamespacePhpTag (nsToken, CurrentNamesapce);

	if (token->type == TOKEN_OPEN_CURLY)
		enterScope (token, NULL, -1);

	deleteToken (nsToken);

	return TRUE;
}
예제 #8
0
파일: FlowParser.cpp 프로젝트: hiwang123/x0
// handlerDecl ::= 'handler' IDENT (';' | [do] stmt)
std::unique_ptr<Handler> FlowParser::handlerDecl()
{
	FNTRACE();

	FlowLocation loc(location());
	nextToken(); // 'handler'

	consume(FlowToken::Ident);
	std::string name = stringValue();
	if (consumeIf(FlowToken::Semicolon)) { // forward-declaration
		loc.update(end());
		return std::make_unique<Handler>(name, loc);
	}

	std::unique_ptr<SymbolTable> st = enterScope("handler-" + name);
	std::unique_ptr<Stmt> body = stmt();
	leaveScope();

	if (!body)
		return nullptr;

	loc.update(body->location().end);

	// forward-declared / previousely -declared?
	if (Handler* handler = scope()->lookup<Handler>(name, Lookup::Self)) {
		if (handler->body() != nullptr) {
			// TODO say where we found the other hand, compared to this one.
			reportError("Redeclaring handler '%s'", handler->name().c_str());
			return nullptr;
		}
		handler->implement(std::move(st), std::move(body));
        handler->owner()->removeSymbol(handler);
        return std::unique_ptr<Handler>(handler);
	}

	return std::make_unique<Handler>(name, std::move(st), std::move(body), loc);
}
예제 #9
0
파일: php.c 프로젝트: blackb1rd/ctags
/* parses a trait:
 * 	trait Foo {} */
static boolean parseTrait (tokenInfo *const token)
{
	boolean readNext = TRUE;
	tokenInfo *name;

	readToken (token);
	if (token->type != TOKEN_IDENTIFIER)
		return FALSE;

	name = newToken ();
	copyToken (name, token, TRUE);

	makeSimplePhpTag (name, K_TRAIT, ACCESS_UNDEFINED);

	readToken (token);
	if (token->type == TOKEN_OPEN_CURLY)
		enterScope (token, name->string, K_TRAIT);
	else
		readNext = FALSE;

	deleteToken (name);

	return readNext;
}
예제 #10
0
파일: php.c 프로젝트: blackb1rd/ctags
static void enterScope (tokenInfo *const parentToken,
						const vString *const extraScope,
						const int parentKind)
{
	tokenInfo *token = newToken ();
	int origParentKind = parentToken->parentKind;

	copyToken (token, parentToken, TRUE);

	if (extraScope)
	{
		token->parentKind = parentKind;
		addToScope (token, extraScope, origParentKind);
	}

	readToken (token);
	while (token->type != TOKEN_EOF &&
		   token->type != TOKEN_CLOSE_CURLY)
	{
		boolean readNext = TRUE;

		switch (token->type)
		{
			case TOKEN_OPEN_CURLY:
				enterScope (token, NULL, -1);
				break;

			case TOKEN_KEYWORD:
				switch (token->keyword)
				{
					/* handle anonymous classes */
					case KEYWORD_new:
						readToken (token);
						if (token->keyword != KEYWORD_class)
							readNext = FALSE;
						else
						{
							char buf[32];
							tokenInfo *name = newToken ();

							copyToken (name, token, TRUE);
							snprintf (buf, sizeof buf, "AnonymousClass%u", ++AnonymousID);
							vStringCopyS (name->string, buf);
							readNext = parseClassOrIface (token, K_CLASS, name);
							deleteToken (name);
						}
						break;

					case KEYWORD_class:		readNext = parseClassOrIface (token, K_CLASS, NULL);		break;
					case KEYWORD_interface:	readNext = parseClassOrIface (token, K_INTERFACE, NULL);	break;
					case KEYWORD_trait:		readNext = parseTrait (token);								break;
					case KEYWORD_function:	readNext = parseFunction (token, NULL);						break;
					case KEYWORD_const:		readNext = parseConstant (token);							break;
					case KEYWORD_define:	readNext = parseDefine (token);								break;

					case KEYWORD_namespace:	readNext = parseNamespace (token);	break;

					case KEYWORD_private:	CurrentStatement.access = ACCESS_PRIVATE;	break;
					case KEYWORD_protected:	CurrentStatement.access = ACCESS_PROTECTED;	break;
					case KEYWORD_public:	CurrentStatement.access = ACCESS_PUBLIC;	break;
					case KEYWORD_var:		CurrentStatement.access = ACCESS_PUBLIC;	break;

					case KEYWORD_abstract:	CurrentStatement.impl = IMPL_ABSTRACT;		break;

					default: break;
				}
				break;

			case TOKEN_VARIABLE:
				readNext = parseVariable (token);
				break;

			default: break;
		}

		if (readNext)
			readToken (token);
	}

	copyToken (parentToken, token, FALSE);
	parentToken->parentKind = origParentKind;
	deleteToken (token);
}
예제 #11
0
파일: php.c 프로젝트: blackb1rd/ctags
/* parse a function
 *
 * if @name is NULL, parses a normal function
 * 	function myfunc($foo, $bar) {}
 * 	function &myfunc($foo, $bar) {}
 * 	function myfunc($foo, $bar) : type {}
 *
 * if @name is not NULL, parses an anonymous function with name @name
 * 	$foo = function($foo, $bar) {}
 * 	$foo = function&($foo, $bar) {}
 * 	$foo = function($foo, $bar) use ($x, &$y) {}
 * 	$foo = function($foo, $bar) use ($x, &$y) : type {} */
static boolean parseFunction (tokenInfo *const token, const tokenInfo *name)
{
	boolean readNext = TRUE;
	accessType access = CurrentStatement.access;
	implType impl = CurrentStatement.impl;
	tokenInfo *nameFree = NULL;

	readToken (token);
	/* skip a possible leading ampersand (return by reference) */
	if (token->type == TOKEN_AMPERSAND)
		readToken (token);

	if (! name)
	{
		if (token->type != TOKEN_IDENTIFIER && token->type != TOKEN_KEYWORD)
			return FALSE;

		name = nameFree = newToken ();
		copyToken (nameFree, token, TRUE);
		readToken (token);
	}

	if (token->type == TOKEN_OPEN_PAREN)
	{
		vString *arglist = vStringNew ();
		int depth = 1;

		vStringPut (arglist, '(');
		do
		{
			readToken (token);

			switch (token->type)
			{
				case TOKEN_OPEN_PAREN:  depth++; break;
				case TOKEN_CLOSE_PAREN: depth--; break;
				default: break;
			}
			/* display part */
			switch (token->type)
			{
				case TOKEN_AMPERSAND:		vStringPut (arglist, '&');		break;
				case TOKEN_CLOSE_CURLY:		vStringPut (arglist, '}');		break;
				case TOKEN_CLOSE_PAREN:		vStringPut (arglist, ')');		break;
				case TOKEN_CLOSE_SQUARE:	vStringPut (arglist, ']');		break;
				case TOKEN_COLON:			vStringPut (arglist, ':');		break;
				case TOKEN_COMMA:			vStringCatS (arglist, ", ");	break;
				case TOKEN_EQUAL_SIGN:		vStringCatS (arglist, " = ");	break;
				case TOKEN_OPEN_CURLY:		vStringPut (arglist, '{');		break;
				case TOKEN_OPEN_PAREN:		vStringPut (arglist, '(');		break;
				case TOKEN_OPEN_SQUARE:		vStringPut (arglist, '[');		break;
				case TOKEN_PERIOD:			vStringPut (arglist, '.');		break;
				case TOKEN_SEMICOLON:		vStringPut (arglist, ';');		break;
				case TOKEN_BACKSLASH:		vStringPut (arglist, '\\');		break;
				case TOKEN_STRING:
				{
					vStringCatS (arglist, "'");	
					vStringCat  (arglist, token->string);
					vStringCatS (arglist, "'");
					break;
				}

				case TOKEN_IDENTIFIER:
				case TOKEN_KEYWORD:
				case TOKEN_VARIABLE:
				{
					switch (vStringLast (arglist))
					{
						case 0:
						case ' ':
						case '{':
						case '(':
						case '[':
						case '.':
						case '\\':
							/* no need for a space between those and the identifier */
							break;

						default:
							vStringPut (arglist, ' ');
							break;
					}
					if (token->type == TOKEN_VARIABLE)
						vStringPut (arglist, '$');
					vStringCat (arglist, token->string);
					break;
				}

				default: break;
			}
		}
		while (token->type != TOKEN_EOF && depth > 0);

		vStringTerminate (arglist);

		makeFunctionTag (name, arglist, access, impl);
		vStringDelete (arglist);

		readToken (token); /* normally it's an open brace or "use" keyword */
	}

	/* skip use(...) */
	if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_use)
	{
		readToken (token);
		skipOverParens (token);
	}

	/* PHP7 return type declaration or if parsing Zephir, skip function return
	 * type hint */
	if ((getInputLanguage () == Lang_php && token->type == TOKEN_COLON) ||
	    (getInputLanguage () == Lang_zephir && token->type == TOKEN_OPERATOR))
	{
		do
			readToken (token);
		while (token->type == TOKEN_IDENTIFIER ||
		       token->type == TOKEN_BACKSLASH);
	}

	if (token->type == TOKEN_OPEN_CURLY)
		enterScope (token, name->string, K_FUNCTION);
	else
		readNext = FALSE;

	if (nameFree)
		deleteToken (nameFree);

	return readNext;
}
예제 #12
0
void check_stmts (tree t) {
	int typeL, typeR, t_start, t_end;
	for (; t != NULL; t = t->next) {
		switch (t->kind) {
			case Procedure: 
				enterScope();	
				fprintf (stderr, "Procedure: scope++ : %d\n", scope);
				handle_decls (t->second);

				check_stmts (t->third);

				printST();
				exitScope();
				fprintf (stderr, "Procedure: scope-- : %d\n", scope);
				break;	

			case Assign:
				if ( t->first->kind != LBrac) { // non-array assignment
					// ST lookup
					if ( ST[t->first->value]->valid && ST[t->first->value]->scope <= scope) { // entry in ST
						typeL = ST[t->first->value]->type; 
						typeR = check_expr(t->second);
					} else { // no visible entry in ST
						fprintf(stderr, "Entry %d-%s invalid in ST:%d\n", t->first->value, id_name (t->first->value), scope);
						error_num++;
						return;
					}	
				} else if (t->first->kind == LBrac) { // array assignment
					// ST array lookup
					if ( ST[t->first->first->value]->valid && ST[t->first->first->value]->type == Array &&
							ST[t->first->first->value]->scope <= scope) { // entry in ST
						typeL = check_expr (t->first);
						typeR = check_expr (t->second);	
					} else { // no visible entry in ST
						fprintf(stderr, "Entry %d-%s invalid in ST:%d\n", 
							t->first->first->value, id_name (t->first->first->value), scope);
						error_num++;
						return;
					}
				}
				if (typeL != typeR) { // LHS and RHS of assignment must match
					type_error(t->kind);
					return;
				}
				break;
			
			case If: case Elsif:
				// expr in If must be Boolean
				if (check_expr(t->first) != Boolean) {
					type_error(t->kind);
					return;	
				}
				check_stmts(t->second);
				check_stmts(t->third);
				break;
			
			case Else:
				check_stmts(t->first);	
				break;

			case For:
				// starts a new scope
				enterScope();
				fprintf (stderr, "For: scope++ : %d\n", scope);
				// 2 range values must be same type
				t_start = check_expr(t->second->first);
				t_end = check_expr(t->second->second);
				if ( t_start != t_end || t_start == NoType) {
					type_error(t->kind);
					return;
				} else {
					// add Ident to ST w/ type of t_start in a new scope
					int pos = t->first->value; // Ident in For loop
					if ( ST[pos]->valid == true) {
						push (ST[pos]); // push prev scope entry to stack
					}
					ST[pos]->index = pos;
					ST[pos]->type = t_start;
					ST[pos]->scope = scope; // will be scope not 2
					char *tmp = id_name(pos);
					ST[pos]->name = tmp;
					ST[pos]->valid = true;	
					ST[pos]->typeSize = -1;
					ST[pos]->addr = -1;
				}

				check_stmts(t->third); // body of For loop

				printST();
				exitScope();
				fprintf (stderr, "For: scope-- : %d\n", scope);
				break;

			// Exit without bool_expr handled in for loop b/c tree will be NULL
			case Exit:
				typeL = check_expr(t->first);
				if ( typeL != Boolean || typeL == NoType) {
					type_error( t->kind);
				}
				break;
			
			case Declare:
				// starts a new scope
				enterScope();
				fprintf (stderr, "Declare: scope++ : %d\n", scope);
				handle_decls (t->first);

				check_stmts (t->second);		

				printST();
				exitScope();
				fprintf (stderr, "Declare: scope-- : %d\n", scope);
				break;
	
			default:
				fprintf(stderr, "No stmt match for token %d\n", t->kind);
	
				// endScope()
		} // end switch statement

	}	// end for loop

} // end check_stmts()
예제 #13
0
void Parser::constructor(string const& name, IntrusiveRefCntPtr<TypeDef> type, TypePtr const& typeRef)
{
	IntrusiveRefCntPtr<TypeConstructor> ctor(new TypeConstructor(name));
	type->constructors.push_back(ctor);
	ctor->type = typeRef;

	expect(TLParen);

	if(token != TRParen)
	{
		do
		{
			bool isLet = test(TLet);

			string name;

			// TODO: Common pattern?
			TypePtr type = maybeType(false);
			if(! type)
			{
				name = expectIdent();
				type = typeName(false);
			}

			ctor->members.push_back(TypeMember(name, type));
			++ctor->ctorParams;
		}
		while(test(TComma));
	}

	{
		string const& ctorName = name;
		// Constructor function
		IntrusiveRefCntPtr<FuncDef> ctorFunc(new FuncDef(ctorName));

		enterFunction(ctorFunc);
		enterScope(new Scope);

		// Constructor function has the same type parameters
		for(auto& p : type->typeParams)
			ctorFunc->typeParams.push_back(p);

		int dummy = 0;

		IntrusiveRefCntPtr<CreateExpr> body(new CreateExpr());

		body->ctor = ctor;

		for(int i = 0; i < ctor->ctorParams; ++i)
		{
			auto& member = ctor->members[i];
			auto p = declParam(ctorFunc, member.name, member.type->fresh(), &dummy);
			body->parameters.push_back(ExprPtr(new VarRef(p)));
		}

		ctorFunc->body = exitScope<Expr>(body);
		ctorFunc->returnType = typeRef;

		// TODO: Check for name collision
		mod->functions[ctorName] = ctorFunc;
		mod->constructorDefs[ctorName] = ctor;
		exitFunction();
	}

	expect(TRParen, true);
}
예제 #14
0
IntrusiveRefCntPtr<FuncDef> Parser::funcDef(bool allowImplicitTypeVars)
{
	string const& name = nextIdent(false);

	// TODO: Always surround this in a type scope?

	IntrusiveRefCntPtr<FuncDef> def(new FuncDef(name));

	int paramCount = 0;
	enterFunction(def);
	enterScope(new Scope);

	if(test(TLParen))
	{
		if(token != TRParen)
		{
			do
			{
				TypePtr type;
				string paramName;
				if(allowImplicitTypeVars)
				{
					paramName = expectIdent();
					type = typeNameOrNewVar(false);
				}
				else
				{
					type = maybeType(false);
					if(! type)
					{
						paramName = expectIdent();
						type = typeName(false);
					}
				}

				declParam(def, paramName, type, &paramCount);
			}
			while(test(TComma));
		}

		expect(TRParen, true);
	}

	TypePtr returnType;
			
	if(firstType[token] || token == TIdent) // TODO: Types may be TIdent too, in certain circumstances
	{
		returnType = typeNameOrNewVar(true);
	}

	if(test(TLBrace))
	{
		auto const& body = statements();
		expect(TRBrace, true);

		def->body = exitScope<Expr>(body);
	}
	
	def->returnType = returnType;
	assert(paramCount == def->parameters.size());

	// TODO: Check for name collision
	mod->functions[def->name] = def;
	exitFunction();

	return def;
}
예제 #15
0
/* parse a function
 *
 * 	function myfunc($foo, $bar) {}
 */
static bool parseFunction (tokenInfo *const token)
{
	bool readNext = true;
	tokenInfo *nameFree = NULL;
	const char *access;

	readToken (token);

	if (token->type != TOKEN_IDENTIFIER)
		return false;

	access = parsePowerShellScope (token);

	nameFree = newToken ();
	copyToken (nameFree, token, true);
	readToken (token);

	if (token->type == TOKEN_OPEN_PAREN)
	{
		vString *arglist = vStringNew ();
		int depth = 1;

		vStringPut (arglist, '(');
		do
		{
			readToken (token);

			switch (token->type)
			{
				case TOKEN_OPEN_PAREN:  depth++; break;
				case TOKEN_CLOSE_PAREN: depth--; break;
				default: break;
			}
			/* display part */
			switch (token->type)
			{
				case TOKEN_CLOSE_CURLY:		vStringPut (arglist, '}');		break;
				case TOKEN_CLOSE_PAREN:		vStringPut (arglist, ')');		break;
				case TOKEN_CLOSE_SQUARE:	vStringPut (arglist, ']');		break;
				case TOKEN_COLON:			vStringPut (arglist, ':');		break;
				case TOKEN_COMMA:			vStringCatS (arglist, ", ");	break;
				case TOKEN_EQUAL_SIGN:		vStringCatS (arglist, " = ");	break;
				case TOKEN_OPEN_CURLY:		vStringPut (arglist, '{');		break;
				case TOKEN_OPEN_PAREN:		vStringPut (arglist, '(');		break;
				case TOKEN_OPEN_SQUARE:		vStringPut (arglist, '[');		break;
				case TOKEN_PERIOD:			vStringPut (arglist, '.');		break;
				case TOKEN_SEMICOLON:		vStringPut (arglist, ';');		break;
				case TOKEN_STRING:			vStringCatS (arglist, "'...'");	break;

				case TOKEN_IDENTIFIER:
				case TOKEN_KEYWORD:
				case TOKEN_VARIABLE:
				{
					switch (vStringLast (arglist))
					{
						case 0:
						case ' ':
						case '{':
						case '(':
						case '[':
						case '.':
							/* no need for a space between those and the identifier */
							break;

						default:
							vStringPut (arglist, ' ');
							break;
					}
					if (token->type == TOKEN_VARIABLE)
						vStringPut (arglist, '$');
					vStringCat (arglist, token->string);
					break;
				}

				default: break;
			}
		}
		while (token->type != TOKEN_EOF && depth > 0);

		makeFunctionTag (nameFree, arglist, access);
		vStringDelete (arglist);

		readToken (token);
	}
	else if (token->type == TOKEN_OPEN_CURLY)
	{	/* filters doesn't need to have an arglist */
		makeFunctionTag (nameFree, NULL, access);
	}

	if (token->type == TOKEN_OPEN_CURLY)
		enterScope (token, nameFree->string, K_FUNCTION);
	else
		readNext = false;

	if (nameFree)
		deleteToken (nameFree);

	return readNext;
}