-
Notifications
You must be signed in to change notification settings - Fork 19
Expressions
Expressions in PawnPlus enable runtime manipulation with dynamically typed objects. An expression tree can be constructed from individual operations and executed to obtain the value of the expression.
An expression is a recursive tree-like structure consisting of various operations and operands. Similarly to iterators, it is an abstract data type and its behaviour depends on the manner it was created in. Expressions are immutable (with one exception), so they can't be modified after their creation. They are also linked by reference with each other, so constructing an expression will not duplicate its arguments.
There are many types of differently behaving expressions. Please see here for detailed listing.
An expression can be created from an initial value or another expression. There are nullary expressions (not depending on any operand), unary expressions (producing a value based on a single operand), binary expressions (two operands), and variadic expressions. PawnPlus also comes with a powerful expression parser supporting all types of expressions and special syntax.
new Expression:arg1 = expr_const(10); // creates a new expression with a constant value of 10
new Expression:arg2 = expr_const(5); // creates a new expression with a constant value of 5
new Expression:add = expr_add(arg1, arg2); // creates a new expression that sums the two expressions
print_s(str_val(expr_get_var(add))); // computes the result of the expression and prints itSince an expression tree cannot be modified once it is created, combining expressions will not duplicate their instances. Neither expr_delete nor garbage collection can delete an expression when it is used by other expressions.
new Expression:arg = expr_const(10);
new Expression:expr = expr_neg(arg); // negation expression
expr_delete(arg); // the expression cannot be truly deleted if it is used
assert !expr_valid(arg); // but the expression reference is not valid anymore
assert expr_get(expr) == -10;Expressions can be used to represent abstract or complex operations via a single object, in which case a parameter must be provided to an expression. For example, there can be a generic function that operates on list elements and the operation is provided externally via an expression:
stock DoSomething(List:l, Expression:e)
{
for_list(it : l)
{
new Variant:v = iter_get_var(it);
v = expr_get_var(expr_bind(e, expr_const_var(v)));
iter_set_var(it, v);
}
}Calling DoSomething with expr_neg(expr_arg(0)) will negate all values in a list (no matter their types). expr_arg(0) represents an external argument given to the expression by code that executes it. Here, expr_bind is used to bind an expression to a constant value (obtained from the iterator). Without binding, the value of expr_arg(0) would be unresolved. The result of the expression is stored back in the list.
expr_bind only fills the initial sublist of arguments used by the expression with the given arguments. The rest is shifted so it can be filled by subsequent calls to expr_bind. For example, expr_bind(expr_add(expr_arg(0), expr_arg(1)), expr_const(10)) binds the value of expr_arg(0) to 10, but the second argument is left unbound. Next call to expr_bind starts at the second argument.
For easier debugging and representing more complex expressions in a more readable syntax, expr_parse can be used to convert a string to an expression tree it represents. The syntax is similar to Pawn, but there are some differences:
- There are no statements; only expressions are supported.
- Newline is not a special character.
-
tagofandsizeofmay evaluate their argument if the result cannot be determined otherwise. -
rankofandaddressofare introduced to assist with manipulating dynamic values and passing them to functions. -
[a,b]is the same as[a][b]. -
$argncan be used to representexpr_arg(n). -
try[main]catch[fallback]corresponds toexpr_try(main, fallback). -
[expr](args)corresponds toexpr_bind(expr, args). -
<expr>corresponds toexpr_quote(expr). - Operations don't follow standard Pawn rules; instead, variant operations semantics are in effect.
- Single cells can be used for reference arguments, but they must be wrapped in
addressof. - Unary operators
*and&corresponding toexpr_valueandexpr_variant. - There is no
>>>operator, and the bit operators only work on objects with a weak tag. - All combinations of syntax allowed by the grammar are syntactically valid; only at execution it is decided whether they are allowed.
- Operations may be performed on expressions instead of on values when executed in some cases, i.e. calling a conditional (
?:) expression calls the expression that is chosen when the condition is evaluated. - Calling a single
Expression:value calls the expression with the provided arguments. - Indexing a single
Expression:value binds it to the arguments.
Expressions are garbage-collected like like strings, variants, and iterators, since most of the references are used as temporary arguments to other expression constructors. In case an expression should remain existing for a longer time, expr_acquire and expr_release should be used. See this for more information about garbage collection.
Expression objects that are kept alive by other expressions will be removed from the pool of active expressions when they are "deleted", but the actual objects will survive until they are no longer used by other expressions. An expression in this state is no longer "valid" and cannot be revived.