Compiler Development Series-Ocelot language 2. Elimination of variable references, compiler-ocelot

Source: Internet
Author: User
Tags variable scope

Compiler Development Series-Ocelot language 2. Elimination of variable references, compiler-ocelot

"Resolution of variable reference" refers to determining the specific variable to which the reference is directed. For example, the variable "I" may be the global variable I, static variable I, or local variable I. This process is used to eliminate such uncertainty and determine which variable is referenced.

To eliminate such uncertainty, we need to associate all variables with their definitions. Such a process is called "Elimination of variable references ". Specifically, the Variable definition (Variable object) is added to all VariableNode objects in the abstract syntax tree that represent referenced variables.

LocalResolver is a class used to handle variable reference resolution. It inherits from Visitor (a series of processes such as "variable reference resolution" and "static type check ). The entry method is:

/* Entry * generates the Scope object tree with ToplevelScope as the root node, and associates all VariableNode with its definition. * // # @ Range/resolve {public void resolve (AST) throws SemanticException {/** Part 1 is a ToplevelScope object, and then the generated ToplevelScope object is used as a scopeStack object. add (toplevel) to scopeStack. In this way, the stack has a Scope object */ToplevelScope toplevel = new ToplevelScope (); scopeStack. add (toplevel);/* add variable definition. * Both foreach statements add global variables, functions, and types to ToplevelScope. * Both of them call ToplevelScope # declareEntity to add a definition or declaration to the ToplevelScope object. * 1st foreach statements add an import file (*. hb) the external variables and functions declared in * 2nd foreach statements are used to import the variables and functions defined in the compiled file * // # @ range/declareToplevel {for (Entity decl: ast. declarations () {toplevel. declareEntity (decl);} for (Entity ent: ast. definitions () {toplevel. defineEntity (ent);} // #// #@ range/resolveRefs {resolveGvarInitializers (ast. definedVariables (); // traverses the global variable resolveConstantValues (ast. constants (); // The initialization expression resolveFu for Traversing constants Nctions (ast. definedFunctions (); // The Most Important // #} toplevel. checkReferences (errorHandler); if (errorHandler. erroccured () {throw new SemanticException ("compile failed. ");}/** In the last part, save the ToplevelScope object and ConstantTable object generated in this class to the AST object. These two objects are used when generating code. to pass information to the next stage, they are saved to the AST object. */Ast. setScope (toplevel); ast. setConstantTable (constantTable);/** so far, the parsing process for variable reference is over. The preceding process generates a tree of Scope objects with ToplevelScope as the root node, and associate all VariableNode with its definition. */}

Add ToplevelScope to the stack. ToplevelScope indicates the scope at the top layer of the program and stores functions and global variables.

Then, add global variables and functions to the top-level scope.

Here we will focus on resolveFunctions (ast. definedFunctions (); this method resolves all functions in the abstract syntax tree:

/** Function Definition processing. * /// # @ range/resolveFunctions {private void resolveFunctions (List <DefinedFunction> funcs) {for (DefinedFunction func: funcs) {// call the pushScope method, generate the scope that contains the function parameters, and apply the scope to the pushScope (func. parameters (); // use resolve (func. body () method to traverse the function's own syntax tree resolve (func. body (); // call the popsmanager method to pop up the Scope object pushed to the stack. Use func. setScope // Add to function func. setScope (popsscope ());}}

Traverse all function nodes. First, all the parameters or defined local variables in a single function node are pushed to the scope of a temporary variable. Then press LocalScope of the temporary variable into scopeStack:

// The pushScope method is to push the new LocalScope object into the scope stack. // #range/pushScope {private void pushScope (List <? Extends DefinedVariable> vars) {// generate () localScope object for parent Scope // currentScope is the method for returning the scope object at the top of the current stack LocalScope Scope = new LocalScope (currentScope ()); /** then use the foreach statement to add the variable vars to the LocalScope object. That is to say, add the variables defined in this scope to the LocalScope object. Especially in the LocalScope at the top of the function, you need to add the definition of the form parameter. */For (DefinedVariable var: vars) {// use scope first. the isDefinedLocally method checks whether the variable if (scope. isDefinedLocally (var. name () {error (var. location (), "duplicated variable in scope:" + var. name ();} // then add. Use the defineVariable method else {scope. defineVariable (var) ;}}/** call scopeStack. addLast (scope) presses the generated LocalScope object to the top of the scope stack. In this way, the nested scope is displayed. */ScopeStack. addLast (scope );}

Then, resolve (func. body () is the function body for resolving the current function, that is, the BlockNode node. Based on java's polymorphism, the following method is finally called:

/** Add a temporary scope. * C language (C♭The program block ({...} block) in will also introduce a new variable scope. * /// # @ Range/BlockNode {public Void visit (BlockNode node) {/** first call the pushScope method to generate a Scope object that stores the variables defined in this Scope, and then press it into the scope stack. */PushScope (node. variables ();/** then execute super. visit (node); executes the processing defined in the base class Visitor, that is, traversing the code of the program block. Visit (VariableNode node) */super. visit (node);/** Finally, use the popsmanager method to pop up the Scope object at the top of the stack, and call the setScope method of the BlockNode object to save the Scope object corresponding to the node. */Node. setScope (popScope (); return null ;}

PushScope (node. variables (); stores the list of temporary variable declarations in the function body in a LocalScope, And then pushes the LocalScope to the scopeStack stack. Next, the most important thing is that super. visit (node); calls the visit method in the Visitor class to dissolve the local variables in the function body,

    public Void visit(BlockNode node) {        for (DefinedVariable var : node.variables()) {            if (var.hasInitializer()) {                visitExpr(var.initializer());            }        }        visitStmts(node.stmts());        return null;    }

Visitemedits is the key, because the variable reference is stored in the VariableNode node and will eventually call:

/** Establish association between VariableNode and variable definition. * use the previous code to successfully generate the Scope object tree. In the following code, you only need to find the tree and reference the parsed code. * // # @ Range/VariableNode {public Void visit (VariableNode node) {try {// use currentScope () first (). get finds the definition Entity ent = currentScope () of the variable in the current scope (). get (node. name ();/** after obtaining the definition, call ent. refered () is used to record the reference information defined, so that a warning is given when the variable is not used. */Ent. refered ();/** you also need to use node. setEntity (ent) to save the definition to the variable node so that you can get the definition of the variable from VariableNode at any time. Establish associations between VariableNode and variable definitions */node. setEntity (ent);}/** if the definition of the variable cannot be found, currentScope (). get will throw a SemanticException and capture it and output it to the error message. */Catch (SemanticException ex) {error (node, ex. getMessage ();} return null ;}

First, find the currentScope () layer closest to the stack. If so, call setEntity to establish association between VariableNode and variable definition. The key here is the get method, which first calls the get of LocalScope:

/* Get the variable definition from the scope tree. * LocalScope # get is the method for getting the variable definition from the scope tree. * First, call variables. get to find the variable named name in the symbol table. If it is found, the variable is returned. If it cannot be found, call the get method of the parent scope (parent) to continue searching. If the parent scope is a LocalScope object, the same method is called for recursive search. If the parent scope is ToplevelScope, call ToplevelScope # get * // # @ range/get {public Entity get (String name) throws SemanticException {DefinedVariable var = variables. get (name); if (var! = Null) {return var;} else {return parent. get (name );}}

If you cannot find the ToplevelScope, keep searching for the parent scope. always find the ToplevelScope get:

/** If the definition of the variable cannot be found in the ToplevelScope by searching for entities, a SemanticException will be thrown because there is no upper-level scope. * // ** Searches and gets entity searching scopes upto ToplevelScope. * // # @ range/get {public Entity get (String name) throws SemanticException {Entity ent = entities. get (name); if (ent = null) {throw new SemanticException ("unresolved reference:" + name) ;}return ent ;}

If no exception is found, an exception is thrown. In general, variables are referenced to find the definition of the latest scope.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.