diff --git a/gsk/gskcodesource.c b/gsk/gskcodesource.c index 155994fdde..92b1cacec2 100644 --- a/gsk/gskcodesource.c +++ b/gsk/gskcodesource.c @@ -146,6 +146,7 @@ gsk_code_source_load (GskCodeSource *source, error)) return NULL; - return g_bytes_new_take (data, length); + source->bytes = g_bytes_new_take (data, length); + return g_bytes_ref (source->bytes); } diff --git a/gsk/gskslcompiler.c b/gsk/gskslcompiler.c index aa001f3d7d..a789ba179b 100644 --- a/gsk/gskslcompiler.c +++ b/gsk/gskslcompiler.c @@ -189,6 +189,47 @@ gsk_sl_compiler_copy_defines (GskSlCompiler *compiler) return copy; } +GskCodeSource * +gsk_sl_compiler_resolve_include (GskSlCompiler *compiler, + GskCodeSource *source, + gboolean local, + const char *name, + GError **error) +{ + GskCodeSource *result; + GBytes *bytes; + + if (local) + { + GFile *source_file = gsk_code_source_get_file (source); + if (source_file) + { + GFile *parent, *file; + + parent = g_file_get_parent (source_file); + file = g_file_resolve_relative_path (parent, name); + result = gsk_code_source_new_for_file (file); + g_object_unref (parent); + g_object_unref (file); + bytes = gsk_code_source_load (result, error); + if (bytes) + { + g_bytes_unref (bytes); + return result; + } + + g_object_unref (result); + } + } + else + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_EXIST, + "Could not resolve \"%s\" in search path.", name); + } + + return NULL; +} + static GskSlProgram * gsk_sl_compiler_compile (GskSlCompiler *compiler, GskCodeSource *source) diff --git a/gsk/gskslcompilerprivate.h b/gsk/gskslcompilerprivate.h index 184502f1f8..32e5660034 100644 --- a/gsk/gskslcompilerprivate.h +++ b/gsk/gskslcompilerprivate.h @@ -25,6 +25,12 @@ G_BEGIN_DECLS GHashTable * gsk_sl_compiler_copy_defines (GskSlCompiler *compiler); +GskCodeSource * gsk_sl_compiler_resolve_include (GskSlCompiler *compiler, + GskCodeSource *source, + gboolean local, + const char *name, + GError **error); + G_END_DECLS #endif /* __GSK_SL_COMPILER_PRIVATE_H__ */ diff --git a/gsk/gskslpreprocessor.c b/gsk/gskslpreprocessor.c index de92b69087..98276c751b 100644 --- a/gsk/gskslpreprocessor.c +++ b/gsk/gskslpreprocessor.c @@ -38,6 +38,7 @@ struct _GskSlPreprocessor GskSlCompiler *compiler; GskSlTokenizer *tokenizer; + GSList *pending_tokenizers; GArray *tokens; GHashTable *defines; gboolean fatal_error; @@ -117,6 +118,7 @@ gsk_sl_preprocessor_unref (GskSlPreprocessor *preproc) g_slist_free (preproc->conditionals); g_hash_table_destroy (preproc->defines); + g_slist_free_full (preproc->pending_tokenizers, (GDestroyNotify) gsk_sl_tokenizer_unref); gsk_sl_tokenizer_unref (preproc->tokenizer); g_object_unref (preproc->compiler); g_array_free (preproc->tokens, TRUE); @@ -167,6 +169,54 @@ gsk_sl_preprocessor_in_ignored_conditional (GskSlPreprocessor *preproc) return FALSE; } +static gboolean +gsk_sl_preprocessor_include (GskSlPreprocessor *preproc, + GskSlPpToken *pp, + gboolean include_local) +{ + GskCodeSource *source; + GError *error = NULL; + + source = gsk_sl_compiler_resolve_include (preproc->compiler, + gsk_sl_tokenizer_get_location (preproc->tokenizer)->source, + include_local, + pp->token.str, + &error); + if (source == NULL) + { + gsk_sl_preprocessor_emit_error (preproc, TRUE, &pp->location, error); + gsk_sl_preprocessor_clear_token (pp); + g_error_free (error); + return FALSE; + } + + if (g_slist_length (preproc->pending_tokenizers) > 20) + { + gsk_sl_preprocessor_error_full (preproc, PREPROCESSOR, &pp->location, "#include nested too deeply."); + gsk_sl_preprocessor_clear_token (pp); + return FALSE; + } + + gsk_sl_preprocessor_clear_token (pp); + pp->location = *gsk_sl_tokenizer_get_location (preproc->tokenizer); + gsk_sl_tokenizer_read_token (preproc->tokenizer, &pp->token); + if (!gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_NEWLINE) && + !gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_EOF)) + { + gsk_sl_preprocessor_error_full (preproc, PREPROCESSOR, &pp->location, "Extra content after #include directive"); + gsk_sl_preprocessor_clear_token (pp); + return FALSE; + } + + gsk_sl_preprocessor_clear_token (pp); + preproc->pending_tokenizers = g_slist_prepend (preproc->pending_tokenizers, preproc->tokenizer); + preproc->tokenizer = gsk_sl_tokenizer_new (source, + gsk_sl_preprocessor_error_func, + preproc, + NULL); + return TRUE; +} + static gboolean gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc, GskSlPpToken *pp, @@ -188,11 +238,29 @@ gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc, return contained_newline || gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_EOF); } +static void +gsk_sl_preprocessor_handle_token (GskSlPreprocessor *preproc, + GskSlPpToken *pp, + gboolean was_newline); + static void gsk_sl_preprocessor_append_token (GskSlPreprocessor *preproc, GskSlPpToken *pp, GSList *used_defines) { + if (gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_EOF) && + preproc->pending_tokenizers) + { + gboolean was_newline; + gsk_sl_tokenizer_unref (preproc->tokenizer); + preproc->tokenizer = preproc->pending_tokenizers->data; + preproc->pending_tokenizers = g_slist_remove (preproc->pending_tokenizers, preproc->tokenizer); + gsk_sl_preprocessor_clear_token (pp); + gsk_sl_preprocessor_next_token (preproc, pp, &was_newline); + gsk_sl_preprocessor_handle_token (preproc, pp, TRUE); + return; + } + if (gsk_sl_preprocessor_in_ignored_conditional (preproc)) { gsk_sl_preprocessor_clear_token (pp); @@ -235,11 +303,6 @@ gsk_sl_preprocessor_append_token (GskSlPreprocessor *preproc, g_array_append_val (preproc->tokens, *pp); } -static void -gsk_sl_preprocessor_handle_token (GskSlPreprocessor *preproc, - GskSlPpToken *pp, - gboolean was_newline); - static void gsk_sl_preprocessor_handle_preprocessor_directive (GskSlPreprocessor *preproc) { @@ -440,6 +503,26 @@ gsk_sl_preprocessor_handle_preprocessor_directive (GskSlPreprocessor *preproc) return; } } + else if (g_str_equal (pp.token.str, "include")) + { + gsk_sl_preprocessor_clear_token (&pp); + if (gsk_sl_preprocessor_next_token (preproc, &pp, &was_newline)) + { + gsk_sl_preprocessor_error_full (preproc, PREPROCESSOR, &pp.location, "No filename after #include."); + gsk_sl_preprocessor_handle_token (preproc, &pp, was_newline); + return; + } + if (gsk_sl_token_is (&pp.token, GSK_SL_TOKEN_STRING)) + { + if (gsk_sl_preprocessor_include (preproc, &pp, TRUE)) + return; + } + else + { + gsk_sl_preprocessor_error_full (preproc, PREPROCESSOR, &pp.location, "Expected filename after #include."); + gsk_sl_preprocessor_clear_token (&pp); + } + } #if 0 else if (g_str_equal (pp.token.str, "line")) {