76 lines
2.7 KiB
JavaScript
76 lines
2.7 KiB
JavaScript
import {Parser} from "./state"
|
|
import {has} from "./util"
|
|
|
|
const pp = Parser.prototype
|
|
|
|
// Object.assign polyfill
|
|
const assign = Object.assign || function(target, ...sources) {
|
|
for (let i = 0; i < sources.length; i++) {
|
|
const source = sources[i]
|
|
for (const key in source) {
|
|
if (has(source, key)) {
|
|
target[key] = source[key]
|
|
}
|
|
}
|
|
}
|
|
return target
|
|
}
|
|
|
|
// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.
|
|
|
|
pp.enterFunctionScope = function() {
|
|
// var: a hash of var-declared names in the current lexical scope
|
|
// lexical: a hash of lexically-declared names in the current lexical scope
|
|
// childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope)
|
|
// parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope)
|
|
this.scopeStack.push({var: {}, lexical: {}, childVar: {}, parentLexical: {}})
|
|
}
|
|
|
|
pp.exitFunctionScope = function() {
|
|
this.scopeStack.pop()
|
|
}
|
|
|
|
pp.enterLexicalScope = function() {
|
|
const parentScope = this.scopeStack[this.scopeStack.length - 1]
|
|
const childScope = {var: {}, lexical: {}, childVar: {}, parentLexical: {}}
|
|
|
|
this.scopeStack.push(childScope)
|
|
assign(childScope.parentLexical, parentScope.lexical, parentScope.parentLexical)
|
|
}
|
|
|
|
pp.exitLexicalScope = function() {
|
|
const childScope = this.scopeStack.pop()
|
|
const parentScope = this.scopeStack[this.scopeStack.length - 1]
|
|
|
|
assign(parentScope.childVar, childScope.var, childScope.childVar)
|
|
}
|
|
|
|
/**
|
|
* A name can be declared with `var` if there are no variables with the same name declared with `let`/`const`
|
|
* in the current lexical scope or any of the parent lexical scopes in this function.
|
|
*/
|
|
pp.canDeclareVarName = function(name) {
|
|
const currentScope = this.scopeStack[this.scopeStack.length - 1]
|
|
|
|
return !has(currentScope.lexical, name) && !has(currentScope.parentLexical, name)
|
|
}
|
|
|
|
/**
|
|
* A name can be declared with `let`/`const` if there are no variables with the same name declared with `let`/`const`
|
|
* in the current scope, and there are no variables with the same name declared with `var` in the current scope or in
|
|
* any child lexical scopes in this function.
|
|
*/
|
|
pp.canDeclareLexicalName = function(name) {
|
|
const currentScope = this.scopeStack[this.scopeStack.length - 1]
|
|
|
|
return !has(currentScope.lexical, name) && !has(currentScope.var, name) && !has(currentScope.childVar, name)
|
|
}
|
|
|
|
pp.declareVarName = function(name) {
|
|
this.scopeStack[this.scopeStack.length - 1].var[name] = true
|
|
}
|
|
|
|
pp.declareLexicalName = function(name) {
|
|
this.scopeStack[this.scopeStack.length - 1].lexical[name] = true
|
|
}
|