///|
priv enum NodeKind {
Identifier
NumberLiteral
StringLiteral
RegexLiteral
TemplateNoSubstitution
Template
TaggedTemplate
BooleanLiteral
NullLiteral
This
Super
ArrayLiteral
ObjectLiteral
Property
PatternIdentifier
PatternArray
PatternObject
PatternProperty
PatternRest
PatternDefault
CoverDefault
FunctionExpression
FunctionDeclaration
ArrowExpression
ClassExpression
ClassDeclaration
Unary
Update
Binary
Logical
Assignment
Conditional
Call
New
Member
Index
Sequence
Spread
Parenthesized
ExpressionStatement
Block
VariableDeclaration
Declarator
If
For
ForIn
ForOf
While
DoWhile
Return
Throw
Break
Continue
Labeled
Switch
SwitchCase
Try
Catch
Debugger
Empty
With
Method
ClassField
ClassStaticBlock
ModuleStatement
} derive(Eq, Debug)
///|
extend NodeKind with @debug.Debug::{to_repr}
///|
priv enum Operator {
Assignment
Add
Subtract
Multiply
Divide
Modulo
Exponent
ShiftLeft
ShiftRight
UnsignedShiftRight
LessThan
GreaterThan
LessThanOrEqual
GreaterThanOrEqual
Equal
NotEqual
StrictlyEqual
StrictlyNotEqual
BitwiseAnd
BitwiseOr
BitwiseXor
LogicalAnd
LogicalOr
NullishCoalescing
In
InstanceOf
LogicalNot
BitwiseNot
Plus
Minus
TypeOf
Void
Delete
Await
Yield
YieldStar
Increment
Decrement
} derive(Eq)
///|
#valtype
priv struct Node {
kind : NodeKind
a : Int
b : Int
c : Int
d : Int
}
///|
fn Node::new(kind : NodeKind, a : Int, b : Int, c : Int, d : Int) -> Node {
{ kind, a, b, c, d, }
}
///|
const DECLARATION_VAR : Int = 0
///|
const DECLARATION_LET : Int = 1
///|
const DECLARATION_CONST : Int = 2
///|
const METHOD_ORDINARY : Int = 0
///|
const METHOD_GETTER : Int = 1
///|
const METHOD_SETTER : Int = 2
///|
const CLASS_MEMBER_STATIC : Int = 1
///|
const CLASS_MEMBER_COMPUTED : Int = 2
///|
const PROPERTY_COMPUTED : Int = 1
///|
const PROPERTY_SHORTHAND : Int = 1
///|
const CHAIN_OPTIONAL : Int = 1
///|
const MEMBER_NEW_TARGET : Int = 2
///|
const UPDATE_PREFIX : Int = 1
///|
const UPDATE_POSTFIX : Int = 0
///|
#valtype
priv struct Function {
name : Int
parameters_offset : Int
parameters_count : Int
body : Int
flags : Int
}
///|
const FUNCTION_ASYNC : Int = 1
///|
const FUNCTION_GENERATOR : Int = 2
///|
const FUNCTION_ARROW : Int = 4
///|
const FUNCTION_EXPRESSION_BODY : Int = 8
///|
#valtype
priv struct Class {
name : Int
superclass : Int
members_offset : Int
members_count : Int
}
///|
#valtype
priv struct Reference {
scope : Int
name_start : Int
name_end : Int
name : Int
symbol : Int
}
///|
#valtype
priv struct Symbol {
name : Int
scope : Int
flags : Int
link : Int
use_count : Int
name_slot : Int
}
///|
let symbol_preserved : Int = 1
///|
#valtype
priv struct Scope {
parent : Int
kind : Int
}
///|
let scope_module : Int = 0
///|
let scope_function : Int = 1
///|
let scope_block : Int = 2
///|
let scope_catch : Int = 3
///|
let scope_class : Int = 4
///|
let scope_parameters : Int = 5
///|
let scope_arrow_parameters : Int = 6
///|
let scope_transparent : Int = 7
///|
priv struct Program {
source : String
nodes : Array[Node]
edges : Array[Int]
references : Array[Reference]
symbols : Array[Symbol]
scopes : Array[Scope]
scope_members : Array[Map[Int, Int]]
functions : Array[Function]
classes : Array[Class]
operators : Array[Operator]
modules : Array[ModuleStatement]
mut root : Int
preserved_names : Map[Int, Bool]
names : Array[String]
generated_names : Array[String]
identifier_ids : IdentifierTable
mut has_dynamic_scope : Bool
mut cover_defaults : Int
character_counts : Array[Int]
mut alphabet : NameAlphabet
property_names : Map[Int, String]
member_names : Map[Int, String]
property_reserved : @hashset.HashSet[String]
}
///|
fn Program::new(source : String) -> Program {
let program : Program = {
source,
nodes: [],
edges: [],
references: [],
symbols: [],
scopes: [],
scope_members: [],
functions: [],
classes: [],
operators: [],
modules: [],
root: -1,
preserved_names: {},
names: ["arguments", "eval", "__proto__"],
generated_names: [],
identifier_ids: IdentifierTable::new(),
has_dynamic_scope: false,
cover_defaults: 0,
character_counts: Array::make(NAME_REST.length(), 0),
alphabet: { first: NAME_FIRST, rest: NAME_REST, },
property_names: {},
member_names: {},
property_reserved: @hashset.HashSet([]),
}
ignore(program.push_scope(-1, scope_module))
program
}
///|
fn Program::node(
self : Program,
kind : NodeKind,
a : Int,
b : Int,
c : Int,
d : Int,
) -> Int {
self.nodes.push(Node::new(kind, a, b, c, d))
self.nodes.length() - 1
}
///|
fn Program::add_reference(self : Program, reference : Reference) -> Int {
self.references.push(reference)
self.references.length() - 1
}
///|
fn Program::add_operator(self : Program, operator : Operator) -> Int {
self.operators.push(operator)
self.operators.length() - 1
}
///|
fn Program::edge_list(self : Program, items : PendingEdges) -> EdgeRange {
let offset = self.edges.length()
let count = items.length()
for item in items.view() {
self.edges.push(item)
}
items.release()
{ offset, count, }
}
///|
fn Program::push_scope(self : Program, parent : Int, kind : Int) -> Int {
self.scope_members.push(Map([]))
self.scopes.push({ parent, kind, })
self.scopes.length() - 1
}
///|
fn Program::add_symbol(
self : Program,
name_start : Int,
name_end : Int,
scope : Int,
) -> Int raise ParseError {
let name = self.identifier(name_start, name_end)
guard self.scope_members[scope].get(name) is Some(id) else {
let id = self.symbols.length()
self.symbols.push({
name,
scope,
flags: 0,
link: -1,
use_count: 0,
name_slot: -1,
})
self.scope_members[scope][name] = id
return id
}
id
}
///|
/// Symbols linked here must keep the same spelling even when JavaScript creates
/// distinct bindings (for example, a catch parameter and a hoisted var).
fn Program::symbol_root(self : Program, id : Int) -> Int {
for current = id {
let next = self.symbols[current].link
guard next >= 0 else { break current }
continue next
}
}
///|
fn Program::link_symbols(self : Program, left : Int, right : Int) -> Unit {
let left_root = self.symbol_root(left)
let right_root = self.symbol_root(right)
guard left_root != right_root else { return }
self.symbols[right_root] = { ..self.symbols[right_root], link: left_root, }
}
///|
let name_arguments : Int = 0
///|
let name_eval : Int = 1
///|
let name_proto : Int = 2
///|
/// Intern source views without copying ordinary identifiers on each reference.
/// Escaped spellings and literal spellings share the same canonical identity.
fn Program::identifier(
self : Program,
start : Int,
end : Int,
) -> Int raise ParseError {
let spelling = self.source[start:end]
guard self.identifier_ids.get(spelling) is Some(id) else {
return self.intern_identifier(spelling)
}
id
}
///|
fn Program::intern_identifier(
self : Program,
spelling : StringView,
) -> Int raise ParseError {
let start = spelling.start_offset()
let end = start + spelling.length()
let escaped = spelling.contains_code_unit('\\')
let name = if escaped {
identifier_name(self.source, start, end)
} else {
spelling.to_owned()
}
let canonical_id = if escaped {
self.identifier_ids.get(name[:])
} else {
None
}
guard canonical_id is Some(id) else {
let id = self.names.length()
self.names.push(name)
self.identifier_ids.insert(spelling, id)
if escaped {
self.identifier_ids.insert(name[:], id)
}
return id
}
self.identifier_ids.insert(spelling, id)
id
}
///|
#valtype
priv struct EdgeRange {
offset : Int
count : Int
}