"scope" 或 "context" 如何在已编译程序中存储和引用?
How is "scope" or "context" stored and referenced in a compiled program?
抱歉,如果我混淆了“作用域”和“上下文”这两个术语,但基本上我指的是我认为的词法作用域,但是当函数(或 class body) 正在评估。
我正在致力于实现一种奇怪的基于树的编程语言,并且具有需要某种“作用域”的不同结构。类似的情况出现在我熟悉的 2 种语言中,JavaScript 和不太熟悉的 Ruby。在 Ruby 中,您有可执行的 class 主体,因此它们有自己的范围,但是您也有具有自己范围的可执行函数。然后在块内部,它们每个都有自己的范围。 “词法”范围树基本上是 variables/stuff 树,您可以在父树的可见代码中引用(可见范围是 词法 范围)。
同样,我想为模块、函数和其他一些东西实现这个,比如我的 views/components(我不想像 React 那样的函数,类似地,我的 lang 中的模块没有被评估职能)。在 JavaScript 中,您只有被调用的函数,并且范围以某种方式存储在每个函数中。
但我想知道的是,什么是数据模型/数据结构/或通常是如何存储和引用范围的“结构”? 字面意思是AST 或任何地方,它是如何工作的?
我想做的是将一个模块包裹在一个“树”对象中,例如:
{
type: 'tree',
scope: {
foo: 'bar',
},
object: module
}
这里的 tree
对象是某种 AST 包装器对象,它引用了 object
上使用的范围和对象本身。这是为了防止对象被额外的 scope
属性 污染,它没有:一个模块在一般意义上没有作用域 属性。模块的resolution是有作用域的,所以说不定树对象其实就是resolution对象
{
type: 'resolution',
scope: {
foo: 'bar',
},
object: module
}
但随后它开始变得毛茸茸。在模块中有 classes,它们有一个 class 作用域和一个实例作用域。或具有实例范围的函数。或者我正在使用的不同自定义对象中的其他范围树。
所以我想做的是将这个解析对象称为“叉子”,并且实质上是构建一个 数据树 。每个 fork 对象都有一个作用域 属性,每个对象实际上都包裹在一个 fork.
中
const moduleFork = {
type: 'fork',
scope: {
foo: 'bar',
},
children: {
// this is a module now.
functions: {
type: 'fork',
children: {}
}
}
}
然后在 functions
对象中,我们有一个函数示例:
// the functions scope is the module itself.
moduleFork.children.functions.scope = moduleFork.children
// then a function func1
moduleFork.children.functions.func1 = {
// this is inside the function now.
type: 'fork',
scope: moduleFork.children,
children: {
params: {
type: 'fork',
list: true,
children: {
param1: {
type: 'fork',
children: {
name: {
type: 'string',
value: 'param1'
},
default: {
type: 'string',
value: 'hello world'
}
}
}
}
}
}
}
我不知道,类似的东西,我还在努力。但是因此,AST 中的每个对象都有一个对范围的引用,它可以用来获取变量值。我省略了一些重复,但实际上它或多或少看起来像这样:
// then a function func1
{
// this is inside the function now.
type: 'fork',
scope: moduleFork.children,
children: {
params: {
type: 'fork',
list: true,
scope: moduleFork.children,
children: {
param1: {
type: 'fork',
scope: moduleFork.children,
children: {
name: {
type: 'string',
value: 'param1'
},
default: {
type: 'string',
value: 'hello world'
}
}
}
}
}
}
}
那样的话,您可以 getFromTree(astNode.scope, 'foo')
并且很简单。同样,由于我们在模块的上下文中,我们可以执行 getFromTree(paramNode.scope, 'functions')
并且它将从模块中获取函数数组(从 fork
包装器之类的东西反序列化)。
在我的语言中,一些 AST 节点会像 { type: 'reference', path: ['foo'] }
,在这种情况下,这个对象知道范围很重要,这样它才能解析 foo
。因此,拥有“包装叉”概念似乎可以轻松传递作用域,尤其是当您拥有事件和数据绑定之类的东西时。而不是像 JavaScript 中那样具有实际的嵌套绑定函数作用域,您只是在处理 AST 树对象。
关键问题是,这在其他编程语言实现中是如何完成的?例如,当 v8 编译 JavaScript 时,是否每个 AST 对象都有对其作用域对象的引用?因此,是否每个 AST 对象都包裹在一个“scopeResolver”对象之类的东西中,就像我在这里尝试做的那样?
如果可以的话,请画出作用域如何工作的数据结构的基本图片,以及它是否像我在这里尝试做的那样在每个对象中都被引用。
V8 在 Block
AST 节点中通过一个名为 Scope
which is referenced 的 class 表示范围,以及在所有其他节点中引入自己的范围,例如 With
, TryCatch
或 FunctionExpression
.
For example, when v8 compiles JavaScript, does every AST object have a reference to its scope object? As such, is each AST object sort of wrapped in a "scopeResolver" object sort of thing, like I am trying to do here?
没有也没有。在解释和编译期间,AST 树总是以深度优先的方式遍历,因此当访问读取或写入变量的节点时,已经遍历了父块。因此,在遍历期间将父块存储在某种堆栈中似乎更容易,而不是在 AST 中添加对每个节点的另一个引用。特别是对于手动内存管理,这似乎是一个没有任何好处的大开销。
据我所知,V8 解释器将作用域保持在 ContextScope
linked list.
抱歉,如果我混淆了“作用域”和“上下文”这两个术语,但基本上我指的是我认为的词法作用域,但是当函数(或 class body) 正在评估。
我正在致力于实现一种奇怪的基于树的编程语言,并且具有需要某种“作用域”的不同结构。类似的情况出现在我熟悉的 2 种语言中,JavaScript 和不太熟悉的 Ruby。在 Ruby 中,您有可执行的 class 主体,因此它们有自己的范围,但是您也有具有自己范围的可执行函数。然后在块内部,它们每个都有自己的范围。 “词法”范围树基本上是 variables/stuff 树,您可以在父树的可见代码中引用(可见范围是 词法 范围)。
同样,我想为模块、函数和其他一些东西实现这个,比如我的 views/components(我不想像 React 那样的函数,类似地,我的 lang 中的模块没有被评估职能)。在 JavaScript 中,您只有被调用的函数,并且范围以某种方式存储在每个函数中。
但我想知道的是,什么是数据模型/数据结构/或通常是如何存储和引用范围的“结构”? 字面意思是AST 或任何地方,它是如何工作的?
我想做的是将一个模块包裹在一个“树”对象中,例如:
{
type: 'tree',
scope: {
foo: 'bar',
},
object: module
}
这里的 tree
对象是某种 AST 包装器对象,它引用了 object
上使用的范围和对象本身。这是为了防止对象被额外的 scope
属性 污染,它没有:一个模块在一般意义上没有作用域 属性。模块的resolution是有作用域的,所以说不定树对象其实就是resolution对象
{
type: 'resolution',
scope: {
foo: 'bar',
},
object: module
}
但随后它开始变得毛茸茸。在模块中有 classes,它们有一个 class 作用域和一个实例作用域。或具有实例范围的函数。或者我正在使用的不同自定义对象中的其他范围树。
所以我想做的是将这个解析对象称为“叉子”,并且实质上是构建一个 数据树 。每个 fork 对象都有一个作用域 属性,每个对象实际上都包裹在一个 fork.
中const moduleFork = {
type: 'fork',
scope: {
foo: 'bar',
},
children: {
// this is a module now.
functions: {
type: 'fork',
children: {}
}
}
}
然后在 functions
对象中,我们有一个函数示例:
// the functions scope is the module itself.
moduleFork.children.functions.scope = moduleFork.children
// then a function func1
moduleFork.children.functions.func1 = {
// this is inside the function now.
type: 'fork',
scope: moduleFork.children,
children: {
params: {
type: 'fork',
list: true,
children: {
param1: {
type: 'fork',
children: {
name: {
type: 'string',
value: 'param1'
},
default: {
type: 'string',
value: 'hello world'
}
}
}
}
}
}
}
我不知道,类似的东西,我还在努力。但是因此,AST 中的每个对象都有一个对范围的引用,它可以用来获取变量值。我省略了一些重复,但实际上它或多或少看起来像这样:
// then a function func1
{
// this is inside the function now.
type: 'fork',
scope: moduleFork.children,
children: {
params: {
type: 'fork',
list: true,
scope: moduleFork.children,
children: {
param1: {
type: 'fork',
scope: moduleFork.children,
children: {
name: {
type: 'string',
value: 'param1'
},
default: {
type: 'string',
value: 'hello world'
}
}
}
}
}
}
}
那样的话,您可以 getFromTree(astNode.scope, 'foo')
并且很简单。同样,由于我们在模块的上下文中,我们可以执行 getFromTree(paramNode.scope, 'functions')
并且它将从模块中获取函数数组(从 fork
包装器之类的东西反序列化)。
在我的语言中,一些 AST 节点会像 { type: 'reference', path: ['foo'] }
,在这种情况下,这个对象知道范围很重要,这样它才能解析 foo
。因此,拥有“包装叉”概念似乎可以轻松传递作用域,尤其是当您拥有事件和数据绑定之类的东西时。而不是像 JavaScript 中那样具有实际的嵌套绑定函数作用域,您只是在处理 AST 树对象。
关键问题是,这在其他编程语言实现中是如何完成的?例如,当 v8 编译 JavaScript 时,是否每个 AST 对象都有对其作用域对象的引用?因此,是否每个 AST 对象都包裹在一个“scopeResolver”对象之类的东西中,就像我在这里尝试做的那样?
如果可以的话,请画出作用域如何工作的数据结构的基本图片,以及它是否像我在这里尝试做的那样在每个对象中都被引用。
V8 在 Block
AST 节点中通过一个名为 Scope
which is referenced 的 class 表示范围,以及在所有其他节点中引入自己的范围,例如 With
, TryCatch
或 FunctionExpression
.
For example, when v8 compiles JavaScript, does every AST object have a reference to its scope object? As such, is each AST object sort of wrapped in a "scopeResolver" object sort of thing, like I am trying to do here?
没有也没有。在解释和编译期间,AST 树总是以深度优先的方式遍历,因此当访问读取或写入变量的节点时,已经遍历了父块。因此,在遍历期间将父块存储在某种堆栈中似乎更容易,而不是在 AST 中添加对每个节点的另一个引用。特别是对于手动内存管理,这似乎是一个没有任何好处的大开销。
据我所知,V8 解释器将作用域保持在 ContextScope
linked list.