From 364d83b199afa6a341173a80791060a42c169f0c Mon Sep 17 00:00:00 2001 From: Udo Date: Mon, 18 Apr 2022 23:37:21 +0000 Subject: [PATCH] doc update --- doc/areas/ob.txt | 6 +- doc/areas/string.txt | 2 + doc/index.uce | 2 +- doc/pages/call_file.txt | 23 ++ doc/pages/concat.txt | 11 + doc/pages/{1_#load.txt => load.txt} | 2 + doc/pages/print.txt | 3 +- doc/pages/render_file.txt | 16 ++ doc/singlepage.uce | 142 +++++++++++++ src/lib/compiler.cpp | 31 ++- src/lib/compiler.h | 3 +- src/lib/types.h | 10 +- test/call_file.uce | 21 ++ test/call_file_funcs.uce | 6 + test/index.uce | 1 + test/script-example1.usp | 6 + test/script.uce | 317 +++++++++++++++++++--------- 17 files changed, 482 insertions(+), 120 deletions(-) create mode 100644 doc/pages/call_file.txt create mode 100644 doc/pages/concat.txt rename doc/pages/{1_#load.txt => load.txt} (93%) create mode 100644 doc/pages/render_file.txt create mode 100644 doc/singlepage.uce create mode 100644 test/call_file.uce create mode 100644 test/call_file_funcs.uce create mode 100644 test/script-example1.usp diff --git a/doc/areas/ob.txt b/doc/areas/ob.txt index f659204..412319a 100644 --- a/doc/areas/ob.txt +++ b/doc/areas/ob.txt @@ -1,6 +1,10 @@ -Output Buffer Functions +Output / Invocation Functions +call_file +load ob_clear ob_get ob_get_clear ob_start +print +render_file diff --git a/doc/areas/string.txt b/doc/areas/string.txt index 23d8efc..914c1b4 100644 --- a/doc/areas/string.txt +++ b/doc/areas/string.txt @@ -1,9 +1,11 @@ String Functions +concat filter first join nibble +print split split_space split_utf8 diff --git a/doc/index.uce b/doc/index.uce index 70baf47..74d7152 100644 --- a/doc/index.uce +++ b/doc/index.uce @@ -11,7 +11,7 @@ void render_see_section(String name) } else if(line != "") { - <>
  • ()
  • + <>
  • ()
  • } idx += 1; } diff --git a/doc/pages/call_file.txt b/doc/pages/call_file.txt new file mode 100644 index 0000000..7e83b01 --- /dev/null +++ b/doc/pages/call_file.txt @@ -0,0 +1,23 @@ +:sig +DTree* call_file(String file_name, String function_name, DTree* call_param = null) + +:params +file_name : UCE file to load and execute +function_name : name of the function to invoke +call_param : optional, call parameter +return value : DTree* returned from function + +:desc +Calls a function inside a UCE file. + +:Example +// export a function +EXPORT void test_func() +{ + print("HELLO FROM TEST FUNCTION"); +} +// use that function in another file +call_file("call_file_funcs.uce", "test_func"); + +:see +>ob diff --git a/doc/pages/concat.txt b/doc/pages/concat.txt new file mode 100644 index 0000000..30c7328 --- /dev/null +++ b/doc/pages/concat.txt @@ -0,0 +1,11 @@ +:sig +String concat(...vals) + +:params +...val : one or more values that should be concatenated + +:desc +Returns a string with all the parameters concatenated into one. + +:see +>string diff --git a/doc/pages/1_#load.txt b/doc/pages/load.txt similarity index 93% rename from doc/pages/1_#load.txt rename to doc/pages/load.txt index 83a2087..e290fb3 100644 --- a/doc/pages/1_#load.txt +++ b/doc/pages/load.txt @@ -7,3 +7,5 @@ file name : name of an UCE file that should be included :desc Includes another UCE file +:see +>ob diff --git a/doc/pages/print.txt b/doc/pages/print.txt index 328529d..ae3d19d 100644 --- a/doc/pages/print.txt +++ b/doc/pages/print.txt @@ -7,4 +7,5 @@ void print(...val) :desc Appends data to the current request's output stream. - +:see +>string diff --git a/doc/pages/render_file.txt b/doc/pages/render_file.txt new file mode 100644 index 0000000..ddb7b0f --- /dev/null +++ b/doc/pages/render_file.txt @@ -0,0 +1,16 @@ +:sig +void render_file(String file_name, DTree& call_param = null) + +:params +file_name : UCE file to load and execute +call_param : optional, call parameter + +:desc +Calls another UCE file and executes its RENDER() function. + +:Example +// call a common page template +render_file("page-template.uce"); + +:see +>ob diff --git a/doc/singlepage.uce b/doc/singlepage.uce new file mode 100644 index 0000000..6e7336e --- /dev/null +++ b/doc/singlepage.uce @@ -0,0 +1,142 @@ +StringMap* already_shown_items; + +void render_doc_page(String page) +{ + (*already_shown_items)[page] = "Y"; + auto doc = split(file_get_contents("pages/"+page+".txt"), "\n"); + String layout_class = "text"; + u32 line_idx = 0; + for(auto s : doc) + { + line_idx++; + if(s == "") + { + + } + else if(s.substr(0, 1) == ":") + { + layout_class = s.substr(1); + if(line_idx > 1) + { + <> + } + <>
    + if(layout_class == "params") + { + <>

    Parameters

    + } + else if(layout_class == "sig") + { + String page_name = page; + if(page[1] == '_') + { + nibble(page_name, "_"); + } + <>

    + } + else if(layout_class == "pre") + { + layout_class = "sig"; + } + else if(layout_class == "desc") + { + <>

    Description

    + } + else if(layout_class == "see") + { + + } + else + { + <>

    + } + } + else + { + if(s.substr(0, 1) == "-") + { + nibble(s, "-"); + <>
  • + } + else if(layout_class == "sig") + { + <>
    + } + else if(layout_class == "params") + { + <>
    :
    + } + else if(layout_class == "see") + { + if(s[0] == '>') + { + //render_see_section(s.substr(1)); + } + else + { + <>
    ()
    + } + } + else + { + <>
    + } + } + } +} + +void render_see_section(String name) +{ + StringList lines = split(file_get_contents("areas/"+name+".txt"), "\n"); + s32 idx = 0; + for(auto line : lines) + { + if(idx == 0) + { + <>

    + } + else if(line != "") + { + render_doc_page(line); + } + idx += 1; + } +} + + +RENDER() +{ + + already_shown_items = new StringMap(); + + String page = first(context->get["p"], "index"); + + <> + + + + +

    + UCE API +

    +

    Other Functions and Data Structures

    + + + +} diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index b96c2bd..452cbde 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -159,20 +159,16 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String pc.append("#include \"" + sub_su->bin_path + "/" + sub_su->pre_file_name + "\"\n"); } } - else if(false && current_line.substr(0, 6) == "EXPORT" && isspace(current_line[6])) + else if(current_line.substr(0, 6) == "EXPORT" && isspace(current_line[6])) { + current_line = ""; auto end_declaration_pos = content.find("{", i); if(end_declaration_pos != std::string::npos) { - // remove string "API " from output - //pc.resize(pc.length() - 7); pc.append(1, '\n'); String declaration = trim(content.substr(i, end_declaration_pos - i)); - String return_type_and_name = nibble(declaration, "("); - StringList rtn_list = split_space(return_type_and_name); - String fn_name = rtn_list.back(); rtn_list.pop_back(); - su->api_declarations.push_back(fn_name + ":" + join(rtn_list, " ") + ":(" + declaration + "\n"); - printf("declaration found: %s\n", declaration.c_str()); + su->api_declarations.push_back(declaration+";\n"); + //printf("declaration found: %s\n", declaration.c_str()); } } } @@ -430,7 +426,20 @@ void render_file(String file_name, DTree& call_param) compiler_invoke(context, file_name, call_param); } -DTree* call_file_function(String file_name, String function_name, DTree* call_param) +SharedUnit* load_file(String file_name) +{ + auto su = compiler_load_shared_unit(context, file_name, "", false); + if(su && su->so_handle) + { + return(su); + } + else + { + return(0); + } +} + +DTree* call_file(String file_name, String function_name, DTree* call_param) { DTree* result; auto su = compiler_load_shared_unit(context, file_name, "", false); @@ -445,7 +454,7 @@ DTree* call_file_function(String file_name, String function_name, DTree* call_pa auto f = (dtree_call_handler)dlsym(su->so_handle, function_name.c_str()); if(!f) { - print("Error: call_file_function() function '", function_name, "' not found"); + print("Error: call_file() function '", function_name, "' not found"); } else { @@ -459,7 +468,7 @@ DTree* call_file_function(String file_name, String function_name, DTree* call_pa } else { - print("Error: call_file_function() could not load unit file '", file_name, "'"); + print("Error: call_file() could not load unit file '", file_name, "'"); } return(result); } diff --git a/src/lib/compiler.h b/src/lib/compiler.h index b43d6ba..a5cadee 100644 --- a/src/lib/compiler.h +++ b/src/lib/compiler.h @@ -10,8 +10,9 @@ SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_opti void compiler_invoke(Request* context, String file_name, DTree& call_param); SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path, bool opt_so_optional = false); +SharedUnit* load_file(String file_name); void render_file(String file_name); void render_file(String file_name, DTree& call_param); -DTree* call_file_function(String file_name, String function_name, DTree* call_param = 0); +DTree* call_file(String file_name, String function_name, DTree* call_param = 0); StringList precompile_jobs; diff --git a/src/lib/types.h b/src/lib/types.h index 04f848b..83e87ec 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -43,7 +43,7 @@ String operator+(String lhs, f32 rhs) { } #define DEBUG_MEMORY_OFF -#define GLOBAL_ARENA_ALLOCATOR +#define NO_GLOBAL_ARENA_ALLOCATOR struct MemoryArena { @@ -293,3 +293,11 @@ void print(Ts... args) ((*context->ob << args), ...); } +template +String concat(Ts... args) +{ + std::stringstream out; + ((out << args), ...); + return(out.str()); +} + diff --git a/test/call_file.uce b/test/call_file.uce new file mode 100644 index 0000000..4987707 --- /dev/null +++ b/test/call_file.uce @@ -0,0 +1,21 @@ + + +RENDER() +{ + + <> + +

    + UCE Test: + call_file() +

    +
    +
    params) ?>
    + + +} + diff --git a/test/call_file_funcs.uce b/test/call_file_funcs.uce new file mode 100644 index 0000000..1fcd914 --- /dev/null +++ b/test/call_file_funcs.uce @@ -0,0 +1,6 @@ +// some bullshit? + +EXPORT void test_func() +{ + print("HELLO FROM TEST FUNCTION"); +} diff --git a/test/index.uce b/test/index.uce index 3d7272d..e87f6b9 100644 --- a/test/index.uce +++ b/test/index.uce @@ -31,6 +31,7 @@ RENDER()
  • RNG/Noise
  • Task API
  • UTF-8
  • +
  • call_file()
  •  c);
    +	print(c);
    diff --git a/test/script.uce b/test/script.uce
    index e3fa89a..a274bdc 100644
    --- a/test/script.uce
    +++ b/test/script.uce
    @@ -1,4 +1,18 @@
     
    +String add_token_type(String tokenstr)
    +{
    +	char c = tokenstr[0];
    +	if(c == 34) // quote character, as number here because of HTML parser bug
    +		return(String("Q") + tokenstr);
    +	if(isalpha(c))
    +		return(String("I") + tokenstr);
    +	if(isdigit(c))
    +		return(String("N") + tokenstr);
    +	if(ispunct(c))
    +		return(String("O") + tokenstr);
    +	return(String("?") + tokenstr);
    +}
    +
     StringList script_tokenize(String code)
     {
     	StringList result;
    @@ -17,7 +31,7 @@ StringList script_tokenize(String code)
     			else if(quote_character == c && last_character != '\\')
     			{
     				mode = 'N';
    -				result.push_back(current_token);
    +				result.push_back(add_token_type(current_token));
     				current_token = "";
     			}
     			else
    @@ -30,7 +44,7 @@ StringList script_tokenize(String code)
     			if(isspace(c))
     			{
     				if(current_token != "")
    -					result.push_back(current_token);
    +					result.push_back(add_token_type(current_token));
     				current_token = "";
     			}
     			else if(c == '"' || c == '\'')
    @@ -38,15 +52,15 @@ StringList script_tokenize(String code)
     				mode = 'Q';
     				quote_character = c;
     				if(current_token != "")
    -					result.push_back(current_token);
    +					result.push_back(add_token_type(current_token));
     				current_token = "\"";
     			}
     			else if(ispunct(c) && c != '_')
     			{
     				if(current_token != "")
    -					result.push_back(current_token);
    +					result.push_back(add_token_type(current_token));
     				current_token = "";
    -				result.push_back(String().append(1, c));
    +				result.push_back(add_token_type(String("").append(1, c)));
     			}
     			else
     			{
    @@ -56,7 +70,7 @@ StringList script_tokenize(String code)
     		last_character = c;
     	}
     	if(current_token != "")
    -		result.push_back(current_token);
    +		result.push_back(add_token_type(current_token));
     	return(result);
     }
     
    @@ -64,40 +78,37 @@ struct ASTNode
     {
     	String identifier = "";
     	String type_name = "";
    +	String string_literal = "";
     	char type = 0;
    -	ASTNode* assign = 0;
    -	ASTNode* call_params = 0;
    -	ASTNode* children = 0;
    -	ASTNode* next = 0;
    +	std::vector assign;
    +	std::vector call_params;
    +	std::vector children;
     
     	ASTNode(char _type)
     	{
    -		type = _type;
    -		type_name = "";
     		identifier = "";
    -		assign = 0;
    -		children = 0;
    -		next = 0;
    +		type_name = "";
    +		string_literal = "";
    +		type = _type;
     	}
     
     	String to_string(String indent = "")
     	{
     		String result = "";
    -		ASTNode* current = this;
    -		while(current)
    -		{
    -			result += indent + String().append(1, current->type);
    -			if(identifier != "") result += " ident=" + identifier;
    -			if(type_name != "")  result += " type=" + type_name;
    -			result += "\n";
    -			if(current->call_params)
    -				result += current->call_params->to_string(indent + "--CALL ");
    -			if(current->assign)
    -				result += current->assign->to_string(indent + "--ASSIGN ");
    -			if(current->children)
    -				result += current->children->to_string(indent + "  ");
    -			current = current->next;
    -		}
    +		result += indent + String().append(1, type);
    +		if(identifier != "")
    +			result += " ident=" + identifier;
    +		if(type_name != "")
    +			result += " type_name=" + type_name;
    +		if(string_literal != "")
    +			result += " " + string_literal;
    +		result += "\n";
    +		for(auto callp : call_params)
    +			result += indent + "  CALLP " + callp->to_string(indent + "  ");
    +		for(auto assign_expr : assign)
    +			result += indent + "  ASSIGN VALUE " + assign_expr->to_string(indent + "  ");
    +		for(auto child : children)
    +			result += indent + "  " + child->to_string(indent + "  ");
     		return(result);
     	}
     
    @@ -106,99 +117,200 @@ struct ASTNode
     struct ParserState
     {
     
    -	ASTNode* root = 0;
    +	ASTNode* root;
     	StringList tokens;
     	s32 token_index = 0;
     	String error = "";
    +	u64 error_location = 0;
     
    -	String next()
    +	String consume(char token_kind = 0)
     	{
    -		return(tokens[token_index++]);
    +		if(error_location > 0) return("");
    +		if(token_kind && look_ahead(0)[0] != token_kind)
    +		{
    +			unexpected_token();
    +		}
    +		printf("consume #%i %s -> %s \n", token_index, look_ahead(0).c_str(), look_ahead(1).c_str());
    +		String tk = look_ahead(0);
    +		token_index++;
    +		return(tk);
     	}
     
    -	String look_ahead(u32 by)
    +	String look_ahead(s32 by)
     	{
    +		if(error_location > 0)
    +			return("_");
    +		if(token_index+by < 0 || token_index+by >= tokens.size())
    +			return("_");
     		return(tokens[token_index+by]);
     	}
     
    -	bool expect(String token_str)
    +	bool more()
     	{
    -		String found = look_ahead(0);
    -		if(found == token_str)
    -			return(true);
    -		token_index = tokens.size() + 1;
    -		error = String("'") + token_str + "' expected but '" + found + "' found";
    -		return(false);
    +		if(error_location > 0)
    +			return(false);
    +		return(token_index < tokens.size());
     	}
     
    -	void consume(String token_str)
    +	void unexpected_token(s32 offset = 0)
     	{
    -		if(expect(token_str))
    -			next();
    +		if(error_location) return;
    +		String found = look_ahead(offset);
    +		error_location = token_index;
    +		token_index = tokens.size() + 1;
    +		error = String("unexpected '") + found.substr(1) + String("' found");
    +	}
    +
    +	bool expect(String token_str, String in_mode)
    +	{
    +		if(error_location) return(false);
    +		String found = look_ahead(0);
    +		if(found == token_str)
    +		{
    +			consume();
    +			return(true);
    +		}
    +		error_location = token_index;
    +		token_index = tokens.size() + 1;
    +		error = String("'") + token_str.substr(1) + "' expected but '" + found.substr(1) + "' found in "+in_mode.substr(1);
    +		return(false);
     	}
     
     };
     
    -ASTNode* script_parse_expression(ParserState* state, String accept_delim);
    +ASTNode* script_parse_expression(ParserState* state, String delim1, String delim2);
    +ASTNode* script_parse_declaration(ParserState* state, String delim1, String delim2);
     
    -ASTNode* script_parse_call_params(ParserState* state)
    +ASTNode* script_parse_declaration(ParserState* state, String delim1, String delim2)
     {
    -	ASTNode* result = new ASTNode('P');
    -	while(state->look_ahead(0) != ")")
    -	{
    -		state->next();
    -	}
    -	state->consume(")");
    -	return(result);
    -}
    +	ASTNode* result = new ASTNode('D');
     
    -ASTNode* script_parse_expression(ParserState* state, String accept_delim)
    -{
    -	ASTNode* result = new ASTNode('E');
    -	String tname = state->next();
    -	String tnx = state->look_ahead(0);
    -	if(tnx == ":")
    +	String ident = state->consume();
    +	if(ident[0] != 'I')
     	{
    -		// declaration
    -		result->type = 'D';
    -		result->identifier = tname;
    -		state->consume(":");
    -		if(state->look_ahead(0) != "=")
    -			result->type_name = state->next();
    -		if(state->look_ahead(0) == "=")
    -		{
    -			state->consume("=");
    -			result->assign = script_parse_expression(state, accept_delim);
    -		}
    -		else
    -		{
    -			state->consume(accept_delim);
    -		}
    +		state->unexpected_token(-1);
     		return(result);
     	}
    -	else if(tnx == "=")
    +	result->identifier = ident.substr(1);
    +
    +	if(!state->expect("O:", "declaration"))
    +		return(result);
    +
    +	String n0 = state->look_ahead(0);
    +	if(n0 == "O=")
     	{
    -		// assignment
    -		result->type = 'A';
    -		result->identifier = tname;
    -		state->consume("=");
    -		result->assign = script_parse_expression(state, accept_delim);
    +		result->type_name = "auto";
    +		result->assign.push_back(script_parse_expression(state, delim1, delim2));
     		return(result);
     	}
     	else
     	{
    -		// who knows
    -		result->type = 'E';
    -		result->identifier = tname;
    -		tnx = state->look_ahead(0);
    -		if(tnx == "(")
    +		result->type_name = state->consume('I').substr(1);
    +		String n2 = state->look_ahead(0);
    +		if(n2 == delim1 || n2 == delim2)
     		{
    -			result->type = 'C';
    -			result->call_params = script_parse_call_params(state);
    +			state->consume();
    +			return(result);
     		}
    -		state->consume(accept_delim);
    +		else if(n2 == "O=")
    +		{
    +			state->expect("O=", "declaration");
    +			result->assign.push_back(script_parse_expression(state, delim1, delim2));
    +			return(result);
    +		}
    +		else
    +		{
    +			state->unexpected_token();
    +			return(result);
    +		}
    +	}
    +
    +	return(result);
    +}
    +
    +ASTNode* parse_single_token(ParserState* state)
    +{
    +	ASTNode* result = new ASTNode('?');
    +
    +	String n = state->consume();
    +	if(n[0] == 'Q')
    +	{
    +		result->type = 'S';
    +		result->string_literal = n.substr(1);
    +	}
    +	else if(n[0] == 'N')
    +	{
    +		result->type = 'N';
    +		result->string_literal = n.substr(1);
    +	}
    +	else if(n[0] == 'I')
    +	{
    +		result->type = 'I';
    +		result->identifier = n.substr(1);
    +	}
    +	else
    +	{
    +		state->unexpected_token(-1);
    +	}
    +	return(result);
    +}
    +
    +ASTNode* script_parse_assignment(ParserState* state, String delim1, String delim2)
    +{
    +	ASTNode* result = new ASTNode('A');
    +	result->identifier = state->consume('I').substr(1);
    +	state->expect("O=", "assignment");
    +	result->assign.push_back(script_parse_expression(state, delim1, delim2));
    +	return(result);
    +}
    +
    +ASTNode* script_parse_expression(ParserState* state, String delim1, String delim2)
    +{
    +	ASTNode* result = new ASTNode('E');
    +
    +	String n = state->look_ahead(0);
    +
    +	if(n == "O(")
    +	{
    +		state->consume();
    +		result->children.push_back(script_parse_expression(state, "O)", ""));
    +	}
    +	else if(n == delim1 || n == delim2)
    +	{
    +		state->consume();
     		return(result);
     	}
    +	else
    +	{
    +		String n2 = state->look_ahead(1);
    +		if(n[0] == 'I' && n2 == "O:")
    +		{
    +			return(script_parse_declaration(state, "O;", ""));
    +		}
    +		else if(n2 == delim1 || n2 == delim2)
    +		{
    +			// single component 'expression'
    +			result = parse_single_token(state);
    +			state->consume();
    +			return(result);
    +		}
    +		else if(n[0] == 'I' && n2 == "O=")
    +		{
    +			result = script_parse_assignment(state, delim1, delim2);
    +			n2 = state->look_ahead(0);
    +			if(n2 != delim1 && n2 != delim2)
    +			{
    +				state->unexpected_token();
    +			}
    +			return(result);
    +		}
    +		else
    +		{
    +			state->unexpected_token();
    +			state->error += " in expression";
    +		}
    +	}
    +
     	return(result);
     }
     
    @@ -206,21 +318,13 @@ ParserState* script_parse(StringList tokens)
     {
     	ParserState* state = new ParserState();
     	state->tokens = tokens;
    -	state->root = new ASTNode('R');
    -	ASTNode* current_node = 0;
    +	state->root = new ASTNode('M');
    +
     	while(state->token_index < state->tokens.size())
     	{
    -		if(!current_node)
    -		{
    -			current_node = script_parse_expression(state, ";");
    -			state->root->children = current_node;
    -		}
    -		else
    -		{
    -			current_node->next = script_parse_expression(state, ";");
    -			current_node = current_node->next;
    -		}
    +		state->root->children.push_back(script_parse_expression(state, "O;", ""));
     	}
    +
     	return(state);
     }
     
    @@ -235,20 +339,25 @@ RENDER()
     		
     	
     
    +	String script_src = first(context->post["code"], file_get_contents("script-example1.usp"));
     
     	<>
     		

    Code

    - +
    post["code"]);
    +			StringList tokens = script_tokenize(script_src);
    +			//print(var_dump(tokens));
     
     			ParserState* ps = script_parse(tokens);
     
    -			print(ps->error+"\n");
    +			if(ps->error_location > 0)
    +			{
    +				print(ps->error, " @ token ", 1+ps->error_location, "\n");
    +			}
     
     			print(ps->root->to_string());