Renamed:
- GetMember to MemberAccess
- SetMember to MemberAssignment
- SetMemberByOperator to MemberAssignmentByOperator
- AssignVariableConsume to VariableAssignmentStatement
Warn on assignment statements like this, for which the OG compiler and the new compiler generate different code:
x := x := expr;
This fixes https://github.com/polserver/polserver/issues/311
The OG compiler doesn't optimize the leftmost assignment to an 'assign-variable-consume',
so a listfile for the above looks like this:
0: decl global #0
1: #
2: global #0
3: global #0
4: :=
5: "hi"
6: := #
7: progend
The new compiler optimizes like this:
0: decl global #0
1: #
2: global #0
3: "hi"
4: :=
5: global0 :=
6: progend
For reference, this is what a single assignment looks like:
0: declare global #0
1: # (consume)
2: "hi" (string) len=2 offset=0x1
3: global #0 :=
4: progend
* Fixed: if too many parameters were passed in a function call, an incorrect error message was displayed, and the compiler could even crash.
Also updated all of the error messages related to function call parameters to include the function name.
Adds:
- model/LocalVariableScopeInfo.h
Stores, for local variable blocks:
- the names of the local variables in the block
- the "base index" of those local variables within the current scope
The code generator uses these to clean up local variables as they go out of scope.
We also need this when writing debug information.
There is one part that differs from const declarations: 0-parameter function calls are allowed (by accident) as const values, but not for default parameter values.
Adds:
- ast/CaseDispatchDefaultSelector: AST node for a "default:" selector
- ast/CaseDispatchGroup: AST node for a collection of selectors and the code to execute
- ast/CaseDispatchGroups: AST node holding all groups in a case statement
- ast/CaseDispatchSelectors: AST node holding all selectors for one group
- ast/CaseStatement: AST node for a whole case statement
- codegen/CaseDispatchGroupVisitor: knows what to put in a case jump dispatch table
- codegen/CaseJumpDataBlock: knows how to build the entries in the data block
Also, detect multiple case selectors with the same value (or default). This is different from the OG compiler, which only detected duplicate `default:` selectors.
Adds:
- analyzer/FlowControlScope: Registers a break/continue scope
- analyzer/FlowControlScopes: Registry for break/continue scopes
- ast/LabelableStatement: Base class for AST nodes that can be labelled (loops and case)
- ast/LoopStatement: Base class for AST nodes for loops (have a break and continue label)
- ast/WhileLoop: AST node for a while loop
Adds:
- analyzer/Constants: holds constant name -> value for lookup
- ast/ConstDeclaration: AST node for a const declaration
- optimizer/ConstValidator: validates that an optimized expression is valid to use as a constant.
the Optimizer converts Identifiers that refer to a constant to the optimized constant value.
Process function call parameters to account for:
- default parameter values
- passing parameters by name
Also handle return within a function (I left this out of the previous PR)
Adds:
- astbuilder/SimpleValueCloner: clones values that are valid as constants and parameter default values.
Adds:
- ast/UserFunction AST node for user-defined functions.
- astbuilder/AvailableUserFunction: reference to a parse tree for a user function.
- The AST builder only generates ASTs for user functions that are actually referenced.
- astbuilder/UserFunctionVisitor: visits (builds an AST for) a parse tree for a user function.
- This happens after the .src or .inc file has been otherwise processed, so this class serves to hook up the correct SourceFileIdentifier to the AST.
Adds:
- ast/Block: a block scope that allows declaring local variables
- ast/IfThenElseStatement: AST node for if..elseif..else..endif statements
- model/FlowControlLabel: provides an anchor for jumps or calls.
- Function calls, break statements, continue statements, and loops will all use these.
Adds:
- analyzer/LocalVariableScope: tracks local variables created within a given scope.
- Instantiated on the stack during semantic analysis, this registers itself with LocalVariableScopes during construction and deregisters itself during destruction.
- Detects unused variables during deregistration
- analyzer/LocalVariableScopes: this is what LocalVariableScope registers with.
- keeps track of the stack of local scopes.
- can provide the current local scope, for var statements.
Other notes:
"Shadowed" variables: this is when a variable in one scope hides a variable in another, like so:
var a := 2;
if (a)
var a := 3;
a := 4;
endif
print(a); // still 2
"debug_variables": We'll use these later when writing debug files.
Adds support for var statements at the global level.
Adds:
- analyzer/Variables: keeps track of the variables in scope (either local or global).
- ast/Identifier: AST node for an identifier.
- The optimizer will replace constant identifiers with their constant value (in a later commit).
- The semantic analyzer will set the variable field for local or global variable identifiers.
- ast/VarStatement: AST node for a var statement.
- A single var statement will generate one VarStatement per variable declared.
- model/Variable: Describes a variable, including its index within its scope.