A type has components, if it is essentially an array of tighly packed
scalar values. In this case, a GskSlValue's data is essentially
ScalarType data[n_components];
This required a lot of reorganization because we have to track if we're
at the start of the document.
On the plus side, we now parse the #version tag and if it's correctly
used, we emit a warning that we don't support it.
Aren't we awesome?
This is basically the source format we use to represent source code.
It can be created either from a file or from a GBytes, so we can use it
to hold all data that can be provided by user input (#defines) or by
actual files.
And when we encounter any strings in the preprocessor, we promptly emit
an error and skip them. But we do that after the preprocessor runs, so
we can access strings inside the preprocessor.
This is the 3rd time at least that I've rewritten it. This time, I've
added a GskSlQualifier struct that contains all the information relevant
to qualifiers. It replaces the previous GskSlDecorationList.
This queries where a statement jumps to after it is done. The enum is
sorted by importance, larger values jump further.
We use this to do 3 things:
1. Error out if the function body statement does not return a value from
a non-void function.
2. Make sure to emit a Return as last instruction in a function body
3. Print a warning about dead code when statements follow a jump.
This is a subclass for pretty-printing. It's not yet very useful, but
will become so once we nest blocks or have way too long statements to
fit on a single line.
This changes GskSlScope to allow multiple functions of the same name.
It also introduces GskSlFunctionMatcher, which does overload matching in
a way that can be used while parsing function calls.
This is the first step towards implementing function overloading.
Instead of having the matches() vfunc the code now can query argument
type and count and uses this to implement a generic matches().
Constructors are checked seperately and manually. They also report 0
arguments.
This includes adding gsk_sl_value_componentwise() which is not something
I particularly like, but it does its job - and will be useful for the
other operations.
Allow calling the function call parsing code with a NULL function. In
that case, we will parse the arguments and then return without emitting
an error. This ensures that the parser is in a sane state and reports
useful errors even if there's a typo in a function call.
Instead of just returning after an error an continuing to parse
wherever, use gsk_sl_preprocessor_sync() to find the next point in the
token stream that looks like a useful way to continue parsing.
Instead of relying on parsing functions to return FALSE on error, we
record that parsing failed inside the preprocessor object and continue
parsing (potentially using dummy objects as the result).
That way, we keep parsing and can emit potentially useful error messages
for the rest of the document.
Now we just need to implement that mentality.
This allows operations on GskSlValues that are arrays, because one can
just do code like:
data = gsk_sl_value_get_data (value);
stride = gsk_sl_value_get_index_stride (type);
for (i = 0; i < gsk_sl_value_get_length (type); i++)
{
do_stuff_with_data (data + i * stride);
}
Actually use this to evaluate layout () expressions.
Unfortunately get_constant() is not implemented in many places, so it
doesn't do much more than the int constant parsing.