Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F98447118
grammar.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Jan 13, 07:00
Size
47 KB
Mime Type
text/x-java
Expires
Wed, Jan 15, 07:00 (2 d)
Engine
blob
Format
Raw Data
Handle
23583379
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
grammar.js
View Options
function
_typeof
(
obj
)
{
if
(
typeof
Symbol
===
"function"
&&
typeof
Symbol
.
iterator
===
"symbol"
)
{
_typeof
=
function
_typeof
(
obj
)
{
return
typeof
obj
;
};
}
else
{
_typeof
=
function
_typeof
(
obj
)
{
return
obj
&&
typeof
Symbol
===
"function"
&&
obj
.
constructor
===
Symbol
&&
obj
!==
Symbol
.
prototype
?
"symbol"
:
typeof
obj
;
};
}
return
_typeof
(
obj
);
}
function
_toConsumableArray
(
arr
)
{
if
(
Array
.
isArray
(
arr
))
{
for
(
var
i
=
0
,
arr2
=
new
Array
(
arr
.
length
);
i
<
arr
.
length
;
i
++
)
{
arr2
[
i
]
=
arr
[
i
];
}
return
arr2
;
}
else
{
return
Array
.
from
(
arr
);
}
}
import
{
codeFrameFromSource
}
from
"@webassemblyjs/helper-code-frame"
;
import
*
as
t
from
"@webassemblyjs/ast"
;
import
{
parse32I
}
from
"./number-literals"
;
import
{
parseString
}
from
"./string-literals"
;
import
{
tokens
,
keywords
}
from
"./tokenizer"
;
function
hasPlugin
(
name
)
{
if
(
name
!==
"wast"
)
throw
new
Error
(
"unknow plugin"
);
return
true
;
}
function
isKeyword
(
token
,
id
)
{
return
token
.
type
===
tokens
.
keyword
&&
token
.
value
===
id
;
}
function
tokenToString
(
token
)
{
if
(
token
.
type
===
"keyword"
)
{
return
"keyword ("
.
concat
(
token
.
value
,
")"
);
}
return
token
.
type
;
}
function
identifierFromToken
(
token
)
{
var
_token$loc
=
token
.
loc
,
end
=
_token$loc
.
end
,
start
=
_token$loc
.
start
;
return
t
.
withLoc
(
t
.
identifier
(
token
.
value
),
end
,
start
);
}
export
function
parse
(
tokensList
,
source
)
{
var
current
=
0
;
var
getUniqueName
=
t
.
getUniqueNameGenerator
();
var
state
=
{
registredExportedElements
:
[]
};
// But this time we're going to use recursion instead of a `while` loop. So we
// define a `walk` function.
function
walk
()
{
var
token
=
tokensList
[
current
];
function
eatToken
()
{
token
=
tokensList
[
++
current
];
}
function
getEndLoc
()
{
var
currentToken
=
token
;
if
(
typeof
currentToken
===
"undefined"
)
{
var
lastToken
=
tokensList
[
tokensList
.
length
-
1
];
currentToken
=
lastToken
;
}
return
currentToken
.
loc
.
end
;
}
function
getStartLoc
()
{
return
token
.
loc
.
start
;
}
function
eatTokenOfType
(
type
)
{
if
(
token
.
type
!==
type
)
{
throw
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"Assertion error: expected token of type "
+
type
+
", given "
+
tokenToString
(
token
));
}
eatToken
();
}
function
parseExportIndex
(
token
)
{
if
(
token
.
type
===
tokens
.
identifier
)
{
var
index
=
identifierFromToken
(
token
);
eatToken
();
return
index
;
}
else
if
(
token
.
type
===
tokens
.
number
)
{
var
_index
=
t
.
numberLiteralFromRaw
(
token
.
value
);
eatToken
();
return
_index
;
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"unknown export index"
+
", given "
+
tokenToString
(
token
));
}();
}
}
function
lookaheadAndCheck
()
{
var
len
=
arguments
.
length
;
for
(
var
i
=
0
;
i
<
len
;
i
++
)
{
var
tokenAhead
=
tokensList
[
current
+
i
];
var
expectedToken
=
i
<
0
||
arguments
.
length
<=
i
?
undefined
:
arguments
[
i
];
if
(
tokenAhead
.
type
===
"keyword"
)
{
if
(
isKeyword
(
tokenAhead
,
expectedToken
)
===
false
)
{
return
false
;
}
}
else
if
(
expectedToken
!==
tokenAhead
.
type
)
{
return
false
;
}
}
return
true
;
}
// TODO(sven): there is probably a better way to do this
// can refactor it if it get out of hands
function
maybeIgnoreComment
()
{
if
(
typeof
token
===
"undefined"
)
{
// Ignore
return
;
}
while
(
token
.
type
===
tokens
.
comment
)
{
eatToken
();
if
(
typeof
token
===
"undefined"
)
{
// Hit the end
break
;
}
}
}
/**
* Parses a memory instruction
*
* WAST:
*
* memory: ( memory <name>? <memory_sig> )
* ( memory <name>? ( export <string> ) <...> )
* ( memory <name>? ( import <string> <string> ) <memory_sig> )
* ( memory <name>? ( export <string> )* ( data <string>* )
* memory_sig: <nat> <nat>?
*
*/
function
parseMemory
()
{
var
id
=
t
.
identifier
(
getUniqueName
(
"memory"
));
var
limits
=
t
.
limit
(
0
);
if
(
token
.
type
===
tokens
.
string
||
token
.
type
===
tokens
.
identifier
)
{
id
=
t
.
identifier
(
token
.
value
);
eatToken
();
}
else
{
id
=
t
.
withRaw
(
id
,
""
);
// preserve anonymous
}
/**
* Maybe data
*/
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
data
))
{
eatToken
();
// (
eatToken
();
// data
// TODO(sven): do something with the data collected here
var
stringInitializer
=
token
.
value
;
eatTokenOfType
(
tokens
.
string
);
// Update limits accordingly
limits
=
t
.
limit
(
stringInitializer
.
length
);
eatTokenOfType
(
tokens
.
closeParen
);
}
/**
* Maybe export
*/
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
export
))
{
eatToken
();
// (
eatToken
();
// export
if
(
token
.
type
!==
tokens
.
string
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Expected string in export"
+
", given "
+
tokenToString
(
token
));
}();
}
var
_name
=
token
.
value
;
eatToken
();
state
.
registredExportedElements
.
push
({
exportType
:
"Memory"
,
name
:
_name
,
id
:
id
});
eatTokenOfType
(
tokens
.
closeParen
);
}
/**
* Memory signature
*/
if
(
token
.
type
===
tokens
.
number
)
{
limits
=
t
.
limit
(
parse32I
(
token
.
value
));
eatToken
();
if
(
token
.
type
===
tokens
.
number
)
{
limits
.
max
=
parse32I
(
token
.
value
);
eatToken
();
}
}
return
t
.
memory
(
limits
,
id
);
}
/**
* Parses a data section
* https://webassembly.github.io/spec/core/text/modules.html#data-segments
*
* WAST:
*
* data: ( data <index>? <offset> <string> )
*/
function
parseData
()
{
// optional memory index
var
memidx
=
0
;
if
(
token
.
type
===
tokens
.
number
)
{
memidx
=
token
.
value
;
eatTokenOfType
(
tokens
.
number
);
// .
}
eatTokenOfType
(
tokens
.
openParen
);
var
offset
;
if
(
token
.
type
===
tokens
.
valtype
)
{
eatTokenOfType
(
tokens
.
valtype
);
// i32
eatTokenOfType
(
tokens
.
dot
);
// .
if
(
token
.
value
!==
"const"
)
{
throw
new
Error
(
"constant expression required"
);
}
eatTokenOfType
(
tokens
.
name
);
// const
var
numberLiteral
=
t
.
numberLiteralFromRaw
(
token
.
value
,
"i32"
);
offset
=
t
.
objectInstruction
(
"const"
,
"i32"
,
[
numberLiteral
]);
eatToken
();
eatTokenOfType
(
tokens
.
closeParen
);
}
else
{
eatTokenOfType
(
tokens
.
name
);
// get_global
var
_numberLiteral
=
t
.
numberLiteralFromRaw
(
token
.
value
,
"i32"
);
offset
=
t
.
instruction
(
"get_global"
,
[
_numberLiteral
]);
eatToken
();
eatTokenOfType
(
tokens
.
closeParen
);
}
var
byteArray
=
parseString
(
token
.
value
);
eatToken
();
// "string"
return
t
.
data
(
t
.
memIndexLiteral
(
memidx
),
offset
,
t
.
byteArray
(
byteArray
));
}
/**
* Parses a table instruction
*
* WAST:
*
* table: ( table <name>? <table_type> )
* ( table <name>? ( export <string> ) <...> )
* ( table <name>? ( import <string> <string> ) <table_type> )
* ( table <name>? ( export <string> )* <elem_type> ( elem <var>* ) )
*
* table_type: <nat> <nat>? <elem_type>
* elem_type: anyfunc
*
* elem: ( elem <var>? (offset <instr>* ) <var>* )
* ( elem <var>? <expr> <var>* )
*/
function
parseTable
()
{
var
name
=
t
.
identifier
(
getUniqueName
(
"table"
));
var
limit
=
t
.
limit
(
0
);
var
elemIndices
=
[];
var
elemType
=
"anyfunc"
;
if
(
token
.
type
===
tokens
.
string
||
token
.
type
===
tokens
.
identifier
)
{
name
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
name
=
t
.
withRaw
(
name
,
""
);
// preserve anonymous
}
while
(
token
.
type
!==
tokens
.
closeParen
)
{
/**
* Maybe export
*/
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
elem
))
{
eatToken
();
// (
eatToken
();
// elem
while
(
token
.
type
===
tokens
.
identifier
)
{
elemIndices
.
push
(
t
.
identifier
(
token
.
value
));
eatToken
();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
else
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
export
))
{
eatToken
();
// (
eatToken
();
// export
if
(
token
.
type
!==
tokens
.
string
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Expected string in export"
+
", given "
+
tokenToString
(
token
));
}();
}
var
exportName
=
token
.
value
;
eatToken
();
state
.
registredExportedElements
.
push
({
exportType
:
"Table"
,
name
:
exportName
,
id
:
name
});
eatTokenOfType
(
tokens
.
closeParen
);
}
else
if
(
isKeyword
(
token
,
keywords
.
anyfunc
))
{
// It's the default value, we can ignore it
eatToken
();
// anyfunc
}
else
if
(
token
.
type
===
tokens
.
number
)
{
/**
* Table type
*/
var
min
=
parseInt
(
token
.
value
);
eatToken
();
if
(
token
.
type
===
tokens
.
number
)
{
var
max
=
parseInt
(
token
.
value
);
eatToken
();
limit
=
t
.
limit
(
min
,
max
);
}
else
{
limit
=
t
.
limit
(
min
);
}
eatToken
();
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token"
+
", given "
+
tokenToString
(
token
));
}();
}
}
if
(
elemIndices
.
length
>
0
)
{
return
t
.
table
(
elemType
,
limit
,
name
,
elemIndices
);
}
else
{
return
t
.
table
(
elemType
,
limit
,
name
);
}
}
/**
* Parses an import statement
*
* WAST:
*
* import: ( import <string> <string> <imkind> )
* imkind: ( func <name>? <func_sig> )
* ( global <name>? <global_sig> )
* ( table <name>? <table_sig> )
* ( memory <name>? <memory_sig> )
*
* global_sig: <type> | ( mut <type> )
*/
function
parseImport
()
{
if
(
token
.
type
!==
tokens
.
string
)
{
throw
new
Error
(
"Expected a string, "
+
token
.
type
+
" given."
);
}
var
moduleName
=
token
.
value
;
eatToken
();
if
(
token
.
type
!==
tokens
.
string
)
{
throw
new
Error
(
"Expected a string, "
+
token
.
type
+
" given."
);
}
var
name
=
token
.
value
;
eatToken
();
eatTokenOfType
(
tokens
.
openParen
);
var
descr
;
if
(
isKeyword
(
token
,
keywords
.
func
))
{
eatToken
();
// keyword
var
fnParams
=
[];
var
fnResult
=
[];
var
typeRef
;
var
fnName
=
t
.
identifier
(
getUniqueName
(
"func"
));
if
(
token
.
type
===
tokens
.
identifier
)
{
fnName
=
identifierFromToken
(
token
);
eatToken
();
}
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
if
(
lookaheadAndCheck
(
keywords
.
type
)
===
true
)
{
eatToken
();
typeRef
=
parseTypeReference
();
}
else
if
(
lookaheadAndCheck
(
keywords
.
param
)
===
true
)
{
eatToken
();
fnParams
.
push
.
apply
(
fnParams
,
_toConsumableArray
(
parseFuncParam
()));
}
else
if
(
lookaheadAndCheck
(
keywords
.
result
)
===
true
)
{
eatToken
();
fnResult
.
push
.
apply
(
fnResult
,
_toConsumableArray
(
parseFuncResult
()));
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in import of type"
+
", given "
+
tokenToString
(
token
));
}();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
if
(
typeof
fnName
===
"undefined"
)
{
throw
new
Error
(
"Imported function must have a name"
);
}
descr
=
t
.
funcImportDescr
(
fnName
,
typeRef
!==
undefined
?
typeRef
:
t
.
signature
(
fnParams
,
fnResult
));
}
else
if
(
isKeyword
(
token
,
keywords
.
global
))
{
eatToken
();
// keyword
if
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// (
eatTokenOfType
(
tokens
.
keyword
);
// mut keyword
var
valtype
=
token
.
value
;
eatToken
();
descr
=
t
.
globalType
(
valtype
,
"var"
);
eatTokenOfType
(
tokens
.
closeParen
);
}
else
{
var
_valtype
=
token
.
value
;
eatTokenOfType
(
tokens
.
valtype
);
descr
=
t
.
globalType
(
_valtype
,
"const"
);
}
}
else
if
(
isKeyword
(
token
,
keywords
.
memory
)
===
true
)
{
eatToken
();
// Keyword
descr
=
parseMemory
();
}
else
if
(
isKeyword
(
token
,
keywords
.
table
)
===
true
)
{
eatToken
();
// Keyword
descr
=
parseTable
();
}
else
{
throw
new
Error
(
"Unsupported import type: "
+
tokenToString
(
token
));
}
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
moduleImport
(
moduleName
,
name
,
descr
);
}
/**
* Parses a block instruction
*
* WAST:
*
* expr: ( block <name>? <block_sig> <instr>* )
* instr: block <name>? <block_sig> <instr>* end <name>?
* block_sig : ( result <type>* )*
*
*/
function
parseBlock
()
{
var
label
=
t
.
identifier
(
getUniqueName
(
"block"
));
var
blockResult
=
null
;
var
instr
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
label
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
label
=
t
.
withRaw
(
label
,
""
);
// preserve anonymous
}
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
if
(
lookaheadAndCheck
(
keywords
.
result
)
===
true
)
{
eatToken
();
blockResult
=
token
.
value
;
eatToken
();
}
else
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
// Instruction
instr
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in block body of type"
+
", given "
+
tokenToString
(
token
));
}();
}
maybeIgnoreComment
();
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
blockInstruction
(
label
,
instr
,
blockResult
);
}
/**
* Parses a if instruction
*
* WAST:
*
* expr:
* ( if <name>? <block_sig> ( then <instr>* ) ( else <instr>* )? )
* ( if <name>? <block_sig> <expr>+ ( then <instr>* ) ( else <instr>* )? )
*
* instr:
* if <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* else <name>? <instr>* end <name>?
*
* block_sig : ( result <type>* )*
*
*/
function
parseIf
()
{
var
blockResult
=
null
;
var
label
=
t
.
identifier
(
getUniqueName
(
"if"
));
var
testInstrs
=
[];
var
consequent
=
[];
var
alternate
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
label
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
label
=
t
.
withRaw
(
label
,
""
);
// preserve anonymous
}
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// (
/**
* Block signature
*/
if
(
isKeyword
(
token
,
keywords
.
result
)
===
true
)
{
eatToken
();
blockResult
=
token
.
value
;
eatTokenOfType
(
tokens
.
valtype
);
eatTokenOfType
(
tokens
.
closeParen
);
continue
;
}
/**
* Then
*/
if
(
isKeyword
(
token
,
keywords
.
then
)
===
true
)
{
eatToken
();
// then
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// Instruction
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
consequent
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in consequent body of type"
+
", given "
+
tokenToString
(
token
));
}();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
eatTokenOfType
(
tokens
.
closeParen
);
continue
;
}
/**
* Alternate
*/
if
(
isKeyword
(
token
,
keywords
.
else
))
{
eatToken
();
// else
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// Instruction
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
alternate
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in alternate body of type"
+
", given "
+
tokenToString
(
token
));
}();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
eatTokenOfType
(
tokens
.
closeParen
);
continue
;
}
/**
* Test instruction
*/
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
testInstrs
.
push
(
parseFuncInstr
());
eatTokenOfType
(
tokens
.
closeParen
);
continue
;
}
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in if body"
+
", given "
+
tokenToString
(
token
));
}();
}
return
t
.
ifInstruction
(
label
,
testInstrs
,
blockResult
,
consequent
,
alternate
);
}
/**
* Parses a loop instruction
*
* WAT:
*
* blockinstr :: 'loop' I:label rt:resulttype (in:instr*) 'end' id?
*
* WAST:
*
* instr :: loop <name>? <block_sig> <instr>* end <name>?
* expr :: ( loop <name>? <block_sig> <instr>* )
* block_sig :: ( result <type>* )*
*
*/
function
parseLoop
()
{
var
label
=
t
.
identifier
(
getUniqueName
(
"loop"
));
var
blockResult
;
var
instr
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
label
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
label
=
t
.
withRaw
(
label
,
""
);
// preserve anonymous
}
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
if
(
lookaheadAndCheck
(
keywords
.
result
)
===
true
)
{
eatToken
();
blockResult
=
token
.
value
;
eatToken
();
}
else
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
// Instruction
instr
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in loop body"
+
", given "
+
tokenToString
(
token
));
}();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
loopInstruction
(
label
,
blockResult
,
instr
);
}
function
parseCallIndirect
()
{
var
typeRef
;
var
params
=
[];
var
results
=
[];
var
instrs
=
[];
while
(
token
.
type
!==
tokens
.
closeParen
)
{
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
type
))
{
eatToken
();
// (
eatToken
();
// type
typeRef
=
parseTypeReference
();
}
else
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
param
))
{
eatToken
();
// (
eatToken
();
// param
/**
* Params can be empty:
* (params)`
*/
if
(
token
.
type
!==
tokens
.
closeParen
)
{
params
.
push
.
apply
(
params
,
_toConsumableArray
(
parseFuncParam
()));
}
}
else
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
result
))
{
eatToken
();
// (
eatToken
();
// result
/**
* Results can be empty:
* (result)`
*/
if
(
token
.
type
!==
tokens
.
closeParen
)
{
results
.
push
.
apply
(
results
,
_toConsumableArray
(
parseFuncResult
()));
}
}
else
{
eatTokenOfType
(
tokens
.
openParen
);
instrs
.
push
(
parseFuncInstr
());
}
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
callIndirectInstruction
(
typeRef
!==
undefined
?
typeRef
:
t
.
signature
(
params
,
results
),
instrs
);
}
/**
* Parses an export instruction
*
* WAT:
*
* export: ( export <string> <exkind> )
* exkind: ( func <var> )
* ( global <var> )
* ( table <var> )
* ( memory <var> )
* var: <nat> | <name>
*
*/
function
parseExport
()
{
if
(
token
.
type
!==
tokens
.
string
)
{
throw
new
Error
(
"Expected string after export, got: "
+
token
.
type
);
}
var
name
=
token
.
value
;
eatToken
();
var
moduleExportDescr
=
parseModuleExportDescr
();
return
t
.
moduleExport
(
name
,
moduleExportDescr
);
}
function
parseModuleExportDescr
()
{
var
startLoc
=
getStartLoc
();
var
type
=
""
;
var
index
;
eatTokenOfType
(
tokens
.
openParen
);
while
(
token
.
type
!==
tokens
.
closeParen
)
{
if
(
isKeyword
(
token
,
keywords
.
func
))
{
type
=
"Func"
;
eatToken
();
index
=
parseExportIndex
(
token
);
}
else
if
(
isKeyword
(
token
,
keywords
.
table
))
{
type
=
"Table"
;
eatToken
();
index
=
parseExportIndex
(
token
);
}
else
if
(
isKeyword
(
token
,
keywords
.
global
))
{
type
=
"Global"
;
eatToken
();
index
=
parseExportIndex
(
token
);
}
else
if
(
isKeyword
(
token
,
keywords
.
memory
))
{
type
=
"Memory"
;
eatToken
();
index
=
parseExportIndex
(
token
);
}
eatToken
();
}
if
(
type
===
""
)
{
throw
new
Error
(
"Unknown export type"
);
}
if
(
index
===
undefined
)
{
throw
new
Error
(
"Exported function must have a name"
);
}
var
node
=
t
.
moduleExportDescr
(
type
,
index
);
var
endLoc
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
node
,
endLoc
,
startLoc
);
}
function
parseModule
()
{
var
name
=
null
;
var
isBinary
=
false
;
var
isQuote
=
false
;
var
moduleFields
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
name
=
token
.
value
;
eatToken
();
}
if
(
hasPlugin
(
"wast"
)
&&
token
.
type
===
tokens
.
name
&&
token
.
value
===
"binary"
)
{
eatToken
();
isBinary
=
true
;
}
if
(
hasPlugin
(
"wast"
)
&&
token
.
type
===
tokens
.
name
&&
token
.
value
===
"quote"
)
{
eatToken
();
isQuote
=
true
;
}
if
(
isBinary
===
true
)
{
var
blob
=
[];
while
(
token
.
type
===
tokens
.
string
)
{
blob
.
push
(
token
.
value
);
eatToken
();
maybeIgnoreComment
();
}
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
binaryModule
(
name
,
blob
);
}
if
(
isQuote
===
true
)
{
var
string
=
[];
while
(
token
.
type
===
tokens
.
string
)
{
string
.
push
(
token
.
value
);
eatToken
();
}
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
quoteModule
(
name
,
string
);
}
while
(
token
.
type
!==
tokens
.
closeParen
)
{
moduleFields
.
push
(
walk
());
if
(
state
.
registredExportedElements
.
length
>
0
)
{
state
.
registredExportedElements
.
forEach
(
function
(
decl
)
{
moduleFields
.
push
(
t
.
moduleExport
(
decl
.
name
,
t
.
moduleExportDescr
(
decl
.
exportType
,
decl
.
id
)));
});
state
.
registredExportedElements
=
[];
}
token
=
tokensList
[
current
];
}
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
module
(
name
,
moduleFields
);
}
/**
* Parses the arguments of an instruction
*/
function
parseFuncInstrArguments
(
signature
)
{
var
args
=
[];
var
namedArgs
=
{};
var
signaturePtr
=
0
;
while
(
token
.
type
===
tokens
.
name
||
isKeyword
(
token
,
keywords
.
offset
))
{
var
key
=
token
.
value
;
eatToken
();
eatTokenOfType
(
tokens
.
equal
);
var
value
=
void
0
;
if
(
token
.
type
===
tokens
.
number
)
{
value
=
t
.
numberLiteralFromRaw
(
token
.
value
);
}
else
{
throw
new
Error
(
"Unexpected type for argument: "
+
token
.
type
);
}
namedArgs
[
key
]
=
value
;
eatToken
();
}
// $FlowIgnore
var
signatureLength
=
signature
.
vector
?
Infinity
:
signature
.
length
;
while
(
token
.
type
!==
tokens
.
closeParen
&&
(
// $FlowIgnore
token
.
type
===
tokens
.
openParen
||
signaturePtr
<
signatureLength
))
{
if
(
token
.
type
===
tokens
.
identifier
)
{
args
.
push
(
t
.
identifier
(
token
.
value
));
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
valtype
)
{
// Handle locals
args
.
push
(
t
.
valtypeLiteral
(
token
.
value
));
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
string
)
{
args
.
push
(
t
.
stringLiteral
(
token
.
value
));
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
number
)
{
args
.
push
(
// TODO(sven): refactor the type signature handling
// https://github.com/xtuc/webassemblyjs/pull/129 is a good start
t
.
numberLiteralFromRaw
(
token
.
value
,
// $FlowIgnore
signature
[
signaturePtr
]
||
"f64"
));
// $FlowIgnore
if
(
!
signature
.
vector
)
{
++
signaturePtr
;
}
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
openParen
)
{
/**
* Maybe some nested instructions
*/
eatToken
();
// Instruction
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
// $FlowIgnore
args
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in nested instruction"
+
", given "
+
tokenToString
(
token
));
}();
}
if
(
token
.
type
===
tokens
.
closeParen
)
{
eatToken
();
}
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in instruction argument"
+
", given "
+
tokenToString
(
token
));
}();
}
}
return
{
args
:
args
,
namedArgs
:
namedArgs
};
}
/**
* Parses an instruction
*
* WAT:
*
* instr :: plaininst
* blockinstr
*
* blockinstr :: 'block' I:label rt:resulttype (in:instr*) 'end' id?
* 'loop' I:label rt:resulttype (in:instr*) 'end' id?
* 'if' I:label rt:resulttype (in:instr*) 'else' id? (in2:intr*) 'end' id?
*
* plaininst :: 'unreachable'
* 'nop'
* 'br' l:labelidx
* 'br_if' l:labelidx
* 'br_table' l*:vec(labelidx) ln:labelidx
* 'return'
* 'call' x:funcidx
* 'call_indirect' x, I:typeuse
*
* WAST:
*
* instr:
* <expr>
* <op>
* block <name>? <block_sig> <instr>* end <name>?
* loop <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* end <name>?
* if <name>? <block_sig> <instr>* else <name>? <instr>* end <name>?
*
* expr:
* ( <op> )
* ( <op> <expr>+ )
* ( block <name>? <block_sig> <instr>* )
* ( loop <name>? <block_sig> <instr>* )
* ( if <name>? <block_sig> ( then <instr>* ) ( else <instr>* )? )
* ( if <name>? <block_sig> <expr>+ ( then <instr>* ) ( else <instr>* )? )
*
* op:
* unreachable
* nop
* br <var>
* br_if <var>
* br_table <var>+
* return
* call <var>
* call_indirect <func_sig>
* drop
* select
* get_local <var>
* set_local <var>
* tee_local <var>
* get_global <var>
* set_global <var>
* <type>.load((8|16|32)_<sign>)? <offset>? <align>?
* <type>.store(8|16|32)? <offset>? <align>?
* current_memory
* grow_memory
* <type>.const <value>
* <type>.<unop>
* <type>.<binop>
* <type>.<testop>
* <type>.<relop>
* <type>.<cvtop>/<type>
*
* func_type: ( type <var> )? <param>* <result>*
*/
function
parseFuncInstr
()
{
var
startLoc
=
getStartLoc
();
maybeIgnoreComment
();
/**
* A simple instruction
*/
if
(
token
.
type
===
tokens
.
name
||
token
.
type
===
tokens
.
valtype
)
{
var
_name2
=
token
.
value
;
var
object
;
eatToken
();
if
(
token
.
type
===
tokens
.
dot
)
{
object
=
_name2
;
eatToken
();
if
(
token
.
type
!==
tokens
.
name
)
{
throw
new
TypeError
(
"Unknown token: "
+
token
.
type
+
", name expected"
);
}
_name2
=
token
.
value
;
eatToken
();
}
if
(
token
.
type
===
tokens
.
closeParen
)
{
var
_endLoc
=
token
.
loc
.
end
;
if
(
typeof
object
===
"undefined"
)
{
return
t
.
withLoc
(
t
.
instruction
(
_name2
),
_endLoc
,
startLoc
);
}
else
{
return
t
.
withLoc
(
t
.
objectInstruction
(
_name2
,
object
,
[]),
_endLoc
,
startLoc
);
}
}
var
signature
=
t
.
signatureForOpcode
(
object
||
""
,
_name2
);
var
_parseFuncInstrArgume
=
parseFuncInstrArguments
(
signature
),
_args
=
_parseFuncInstrArgume
.
args
,
_namedArgs
=
_parseFuncInstrArgume
.
namedArgs
;
var
endLoc
=
token
.
loc
.
end
;
if
(
typeof
object
===
"undefined"
)
{
return
t
.
withLoc
(
t
.
instruction
(
_name2
,
_args
,
_namedArgs
),
endLoc
,
startLoc
);
}
else
{
return
t
.
withLoc
(
t
.
objectInstruction
(
_name2
,
object
,
_args
,
_namedArgs
),
endLoc
,
startLoc
);
}
}
else
if
(
isKeyword
(
token
,
keywords
.
loop
))
{
/**
* Else a instruction with a keyword (loop or block)
*/
eatToken
();
// keyword
return
parseLoop
();
}
else
if
(
isKeyword
(
token
,
keywords
.
block
))
{
eatToken
();
// keyword
return
parseBlock
();
}
else
if
(
isKeyword
(
token
,
keywords
.
call_indirect
))
{
eatToken
();
// keyword
return
parseCallIndirect
();
}
else
if
(
isKeyword
(
token
,
keywords
.
call
))
{
eatToken
();
// keyword
var
index
;
if
(
token
.
type
===
tokens
.
identifier
)
{
index
=
identifierFromToken
(
token
);
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
number
)
{
index
=
t
.
indexLiteral
(
token
.
value
);
eatToken
();
}
var
instrArgs
=
[];
// Nested instruction
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
instrArgs
.
push
(
parseFuncInstr
());
eatTokenOfType
(
tokens
.
closeParen
);
}
if
(
typeof
index
===
"undefined"
)
{
throw
new
Error
(
"Missing argument in call instruciton"
);
}
if
(
instrArgs
.
length
>
0
)
{
return
t
.
callInstruction
(
index
,
instrArgs
);
}
else
{
return
t
.
callInstruction
(
index
);
}
}
else
if
(
isKeyword
(
token
,
keywords
.
if
))
{
eatToken
();
// Keyword
return
parseIf
();
}
else
if
(
isKeyword
(
token
,
keywords
.
module
)
&&
hasPlugin
(
"wast"
))
{
eatToken
();
// In WAST you can have a module as an instruction's argument
// we will cast it into a instruction to not break the flow
// $FlowIgnore
var
module
=
parseModule
();
return
module
;
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected instruction in function body"
+
", given "
+
tokenToString
(
token
));
}();
}
}
/*
* Parses a function
*
* WAT:
*
* functype :: ( 'func' t1:vec(param) t2:vec(result) )
* param :: ( 'param' id? t:valtype )
* result :: ( 'result' t:valtype )
*
* WAST:
*
* func :: ( func <name>? <func_sig> <local>* <instr>* )
* ( func <name>? ( export <string> ) <...> )
* ( func <name>? ( import <string> <string> ) <func_sig> )
* func_sig :: ( type <var> )? <param>* <result>*
* param :: ( param <type>* ) | ( param <name> <type> )
* result :: ( result <type>* )
* local :: ( local <type>* ) | ( local <name> <type> )
*
*/
function
parseFunc
()
{
var
fnName
=
t
.
identifier
(
getUniqueName
(
"func"
));
var
typeRef
;
var
fnBody
=
[];
var
fnParams
=
[];
var
fnResult
=
[];
// name
if
(
token
.
type
===
tokens
.
identifier
)
{
fnName
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
fnName
=
t
.
withRaw
(
fnName
,
""
);
// preserve anonymous
}
maybeIgnoreComment
();
while
(
token
.
type
===
tokens
.
openParen
||
token
.
type
===
tokens
.
name
||
token
.
type
===
tokens
.
valtype
)
{
// Instructions without parens
if
(
token
.
type
===
tokens
.
name
||
token
.
type
===
tokens
.
valtype
)
{
fnBody
.
push
(
parseFuncInstr
());
continue
;
}
eatToken
();
if
(
lookaheadAndCheck
(
keywords
.
param
)
===
true
)
{
eatToken
();
fnParams
.
push
.
apply
(
fnParams
,
_toConsumableArray
(
parseFuncParam
()));
}
else
if
(
lookaheadAndCheck
(
keywords
.
result
)
===
true
)
{
eatToken
();
fnResult
.
push
.
apply
(
fnResult
,
_toConsumableArray
(
parseFuncResult
()));
}
else
if
(
lookaheadAndCheck
(
keywords
.
export
)
===
true
)
{
eatToken
();
parseFuncExport
(
fnName
);
}
else
if
(
lookaheadAndCheck
(
keywords
.
type
)
===
true
)
{
eatToken
();
typeRef
=
parseTypeReference
();
}
else
if
(
lookaheadAndCheck
(
tokens
.
name
)
===
true
||
lookaheadAndCheck
(
tokens
.
valtype
)
===
true
||
token
.
type
===
"keyword"
// is any keyword
)
{
// Instruction
fnBody
.
push
(
parseFuncInstr
());
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in func body"
+
", given "
+
tokenToString
(
token
));
}();
}
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
func
(
fnName
,
typeRef
!==
undefined
?
typeRef
:
t
.
signature
(
fnParams
,
fnResult
),
fnBody
);
}
/**
* Parses shorthand export in func
*
* export :: ( export <string> )
*/
function
parseFuncExport
(
funcId
)
{
if
(
token
.
type
!==
tokens
.
string
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Function export expected a string"
+
", given "
+
tokenToString
(
token
));
}();
}
var
name
=
token
.
value
;
eatToken
();
/**
* Func export shorthand, we trait it as a syntaxic sugar.
* A export ModuleField will be added later.
*
* We give the anonymous function a generated name and export it.
*/
var
id
=
t
.
identifier
(
funcId
.
value
);
state
.
registredExportedElements
.
push
({
exportType
:
"Func"
,
name
:
name
,
id
:
id
});
}
/**
* Parses a type instruction
*
* WAST:
*
* typedef: ( type <name>? ( func <param>* <result>* ) )
*/
function
parseType
()
{
var
id
;
var
params
=
[];
var
result
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
id
=
identifierFromToken
(
token
);
eatToken
();
}
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
func
))
{
eatToken
();
// (
eatToken
();
// func
if
(
token
.
type
===
tokens
.
closeParen
)
{
eatToken
();
// function with an empty signature, we can abort here
return
t
.
typeInstruction
(
id
,
t
.
signature
([],
[]));
}
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
param
))
{
eatToken
();
// (
eatToken
();
// param
params
=
parseFuncParam
();
eatTokenOfType
(
tokens
.
closeParen
);
}
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
result
))
{
eatToken
();
// (
eatToken
();
// result
result
=
parseFuncResult
();
eatTokenOfType
(
tokens
.
closeParen
);
}
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
typeInstruction
(
id
,
t
.
signature
(
params
,
result
));
}
/**
* Parses a function result
*
* WAST:
*
* result :: ( result <type>* )
*/
function
parseFuncResult
()
{
var
results
=
[];
while
(
token
.
type
!==
tokens
.
closeParen
)
{
if
(
token
.
type
!==
tokens
.
valtype
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unexpected token in func result"
+
", given "
+
tokenToString
(
token
));
}();
}
var
valtype
=
token
.
value
;
eatToken
();
results
.
push
(
valtype
);
}
return
results
;
}
/**
* Parses a type reference
*
*/
function
parseTypeReference
()
{
var
ref
;
if
(
token
.
type
===
tokens
.
identifier
)
{
ref
=
identifierFromToken
(
token
);
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
number
)
{
ref
=
t
.
numberLiteralFromRaw
(
token
.
value
);
eatToken
();
}
return
ref
;
}
/**
* Parses a global instruction
*
* WAST:
*
* global: ( global <name>? <global_sig> <instr>* )
* ( global <name>? ( export <string> ) <...> )
* ( global <name>? ( import <string> <string> ) <global_sig> )
*
* global_sig: <type> | ( mut <type> )
*
*/
function
parseGlobal
()
{
var
name
=
t
.
identifier
(
getUniqueName
(
"global"
));
var
type
;
// Keep informations in case of a shorthand import
var
importing
=
null
;
maybeIgnoreComment
();
if
(
token
.
type
===
tokens
.
identifier
)
{
name
=
identifierFromToken
(
token
);
eatToken
();
}
else
{
name
=
t
.
withRaw
(
name
,
""
);
// preserve anonymous
}
/**
* maybe export
*/
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
export
))
{
eatToken
();
// (
eatToken
();
// export
var
exportName
=
token
.
value
;
eatTokenOfType
(
tokens
.
string
);
state
.
registredExportedElements
.
push
({
exportType
:
"Global"
,
name
:
exportName
,
id
:
name
});
eatTokenOfType
(
tokens
.
closeParen
);
}
/**
* maybe import
*/
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
import
))
{
eatToken
();
// (
eatToken
();
// import
var
moduleName
=
token
.
value
;
eatTokenOfType
(
tokens
.
string
);
var
_name3
=
token
.
value
;
eatTokenOfType
(
tokens
.
string
);
importing
=
{
module
:
moduleName
,
name
:
_name3
,
descr
:
undefined
};
eatTokenOfType
(
tokens
.
closeParen
);
}
/**
* global_sig
*/
if
(
token
.
type
===
tokens
.
valtype
)
{
type
=
t
.
globalType
(
token
.
value
,
"const"
);
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// (
if
(
isKeyword
(
token
,
keywords
.
mut
)
===
false
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unsupported global type, expected mut"
+
", given "
+
tokenToString
(
token
));
}();
}
eatToken
();
// mut
type
=
t
.
globalType
(
token
.
value
,
"var"
);
eatToken
();
eatTokenOfType
(
tokens
.
closeParen
);
}
if
(
type
===
undefined
)
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Could not determine global type"
+
", given "
+
tokenToString
(
token
));
}();
}
maybeIgnoreComment
();
var
init
=
[];
if
(
importing
!=
null
)
{
importing
.
descr
=
type
;
init
.
push
(
t
.
moduleImport
(
importing
.
module
,
importing
.
name
,
importing
.
descr
));
}
/**
* instr*
*/
while
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
init
.
push
(
parseFuncInstr
());
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
global
(
type
,
init
,
name
);
}
/**
* Parses a function param
*
* WAST:
*
* param :: ( param <type>* ) | ( param <name> <type> )
*/
function
parseFuncParam
()
{
var
params
=
[];
var
id
;
var
valtype
;
if
(
token
.
type
===
tokens
.
identifier
)
{
id
=
token
.
value
;
eatToken
();
}
if
(
token
.
type
===
tokens
.
valtype
)
{
valtype
=
token
.
value
;
eatToken
();
params
.
push
({
id
:
id
,
valtype
:
valtype
});
/**
* Shorthand notation for multiple anonymous parameters
* @see https://webassembly.github.io/spec/core/text/types.html#function-types
* @see https://github.com/xtuc/webassemblyjs/issues/6
*/
if
(
id
===
undefined
)
{
while
(
token
.
type
===
tokens
.
valtype
)
{
valtype
=
token
.
value
;
eatToken
();
params
.
push
({
id
:
undefined
,
valtype
:
valtype
});
}
}
}
else
{
// ignore
}
return
params
;
}
/**
* Parses an element segments instruction
*
* WAST:
*
* elem: ( elem <var>? (offset <instr>* ) <var>* )
* ( elem <var>? <expr> <var>* )
*
* var: <nat> | <name>
*/
function
parseElem
()
{
var
tableIndex
=
t
.
indexLiteral
(
0
);
var
offset
=
[];
var
funcs
=
[];
if
(
token
.
type
===
tokens
.
identifier
)
{
tableIndex
=
identifierFromToken
(
token
);
eatToken
();
}
if
(
token
.
type
===
tokens
.
number
)
{
tableIndex
=
t
.
indexLiteral
(
token
.
value
);
eatToken
();
}
while
(
token
.
type
!==
tokens
.
closeParen
)
{
if
(
lookaheadAndCheck
(
tokens
.
openParen
,
keywords
.
offset
))
{
eatToken
();
// (
eatToken
();
// offset
while
(
token
.
type
!==
tokens
.
closeParen
)
{
eatTokenOfType
(
tokens
.
openParen
);
offset
.
push
(
parseFuncInstr
());
eatTokenOfType
(
tokens
.
closeParen
);
}
eatTokenOfType
(
tokens
.
closeParen
);
}
else
if
(
token
.
type
===
tokens
.
identifier
)
{
funcs
.
push
(
t
.
identifier
(
token
.
value
));
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
number
)
{
funcs
.
push
(
t
.
indexLiteral
(
token
.
value
));
eatToken
();
}
else
if
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
// (
offset
.
push
(
parseFuncInstr
());
eatTokenOfType
(
tokens
.
closeParen
);
}
else
{
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unsupported token in elem"
+
", given "
+
tokenToString
(
token
));
}();
}
}
return
t
.
elem
(
tableIndex
,
offset
,
funcs
);
}
/**
* Parses the start instruction in a module
*
* WAST:
*
* start: ( start <var> )
* var: <nat> | <name>
*
* WAT:
* start ::= ‘(’ ‘start’ x:funcidx ‘)’
*/
function
parseStart
()
{
if
(
token
.
type
===
tokens
.
identifier
)
{
var
index
=
identifierFromToken
(
token
);
eatToken
();
return
t
.
start
(
index
);
}
if
(
token
.
type
===
tokens
.
number
)
{
var
_index2
=
t
.
indexLiteral
(
token
.
value
);
eatToken
();
return
t
.
start
(
_index2
);
}
throw
new
Error
(
"Unknown start, token: "
+
tokenToString
(
token
));
}
if
(
token
.
type
===
tokens
.
openParen
)
{
eatToken
();
var
startLoc
=
getStartLoc
();
if
(
isKeyword
(
token
,
keywords
.
export
))
{
eatToken
();
var
node
=
parseExport
();
var
_endLoc2
=
getEndLoc
();
return
t
.
withLoc
(
node
,
_endLoc2
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
loop
))
{
eatToken
();
var
_node
=
parseLoop
();
var
_endLoc3
=
getEndLoc
();
return
t
.
withLoc
(
_node
,
_endLoc3
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
func
))
{
eatToken
();
var
_node2
=
parseFunc
();
var
_endLoc4
=
getEndLoc
();
maybeIgnoreComment
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node2
,
_endLoc4
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
module
))
{
eatToken
();
var
_node3
=
parseModule
();
var
_endLoc5
=
getEndLoc
();
return
t
.
withLoc
(
_node3
,
_endLoc5
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
import
))
{
eatToken
();
var
_node4
=
parseImport
();
var
_endLoc6
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node4
,
_endLoc6
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
block
))
{
eatToken
();
var
_node5
=
parseBlock
();
var
_endLoc7
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node5
,
_endLoc7
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
memory
))
{
eatToken
();
var
_node6
=
parseMemory
();
var
_endLoc8
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node6
,
_endLoc8
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
data
))
{
eatToken
();
var
_node7
=
parseData
();
var
_endLoc9
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node7
,
_endLoc9
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
table
))
{
eatToken
();
var
_node8
=
parseTable
();
var
_endLoc10
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node8
,
_endLoc10
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
global
))
{
eatToken
();
var
_node9
=
parseGlobal
();
var
_endLoc11
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node9
,
_endLoc11
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
type
))
{
eatToken
();
var
_node10
=
parseType
();
var
_endLoc12
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node10
,
_endLoc12
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
start
))
{
eatToken
();
var
_node11
=
parseStart
();
var
_endLoc13
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node11
,
_endLoc13
,
startLoc
);
}
if
(
isKeyword
(
token
,
keywords
.
elem
))
{
eatToken
();
var
_node12
=
parseElem
();
var
_endLoc14
=
getEndLoc
();
eatTokenOfType
(
tokens
.
closeParen
);
return
t
.
withLoc
(
_node12
,
_endLoc14
,
startLoc
);
}
var
instruction
=
parseFuncInstr
();
var
endLoc
=
getEndLoc
();
maybeIgnoreComment
();
if
(
_typeof
(
instruction
)
===
"object"
)
{
if
(
typeof
token
!==
"undefined"
)
{
eatTokenOfType
(
tokens
.
closeParen
);
}
return
t
.
withLoc
(
instruction
,
endLoc
,
startLoc
);
}
}
if
(
token
.
type
===
tokens
.
comment
)
{
var
_startLoc
=
getStartLoc
();
var
builder
=
token
.
opts
.
type
===
"leading"
?
t
.
leadingComment
:
t
.
blockComment
;
var
_node13
=
builder
(
token
.
value
);
eatToken
();
// comment
var
_endLoc15
=
getEndLoc
();
return
t
.
withLoc
(
_node13
,
_endLoc15
,
_startLoc
);
}
throw
function
()
{
return
new
Error
(
"\n"
+
codeFrameFromSource
(
source
,
token
.
loc
)
+
"\n"
+
"Unknown token"
+
", given "
+
tokenToString
(
token
));
}();
}
var
body
=
[];
while
(
current
<
tokensList
.
length
)
{
body
.
push
(
walk
());
}
return
t
.
program
(
body
);
}
Event Timeline
Log In to Comment