| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 |
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
23x
90x
90x
1352x
90x
90x
90x
90x
| /**
* Semantic Analysis
*
* The semantic analyzer below accepts a Walt AST and maps it, returning a new
* transformed AST which contains all necessary data to generate the final
* WebAssembly binary.
*
* The transformations may or may not create new nodes or attach metadata to
* existing nodes.
*
* Metadata is information necessary to generate a valid binary, like type info.
*/
// @flow
import { combineParsers } from '../plugin';
import { map } from 'walt-parser-tools/map-node';
import { enter as enterScope } from 'walt-parser-tools/scope';
import { AST_METADATA } from './metadata';
import core from '../core';
import base from '../base';
import _types from '../core/types';
import unary from '../core/unary';
import _function from '../core/function';
import _imports from '../core/imports';
import booleans from '../core/bool';
import array from '../core/array';
import memory from '../core/memory';
import _statics from '../core/statics';
import functionPointer from '../core/function-pointer';
import struct from '../core/struct';
import native from '../core/native';
import defaultArguments from '../syntax-sugar/default-arguments';
import sizeof from '../syntax-sugar/sizeof';
import { GLOBAL_INDEX } from './metadata.js';
import type {
NodeType,
Context,
SemanticOptions,
SemanticsFactory,
} from '../flow/types';
export const builtinSemantics = [
base,
core,
_imports,
_types,
unary,
_function,
booleans,
array,
memory,
_statics,
functionPointer,
struct,
native,
sizeof,
defaultArguments,
];
const getBuiltInParsers = (): SemanticsFactory[] => {
return [
base().semantics,
core().semantics,
_imports().semantics,
_types().semantics,
unary().semantics,
_function().semantics,
booleans().semantics,
array().semantics,
memory().semantics,
_statics().semantics,
functionPointer().semantics,
struct().semantics,
native().semantics,
sizeof().semantics,
defaultArguments().semantics,
];
};
// Return AST with full transformations applied
function semantics(
ast: NodeType,
extraSemantics: SemanticsFactory[],
options: SemanticOptions
): NodeType {
// Generate all the plugin instances with proper options
const plugins = [...getBuiltInParsers(), ...extraSemantics];
// Here each semantics parser will receive a reference to the parser & fragment
// this allows a semantic parser to utilize the same grammar rules as the rest
// of the program.
const combined = combineParsers(plugins.map(p => p(options)));
// The context is what we use to transfer state from one parser to another.
// Global state like type information and scope chains for example.
const context: Context = {
functions: {},
types: {},
userTypes: {},
table: {},
hoist: [],
statics: {},
path: [],
scopes: enterScope([], GLOBAL_INDEX),
memories: [],
tables: [],
};
// Parse the current ast
const parsed = map(combined)([ast, context]);
const { functions, scopes, types, userTypes, statics, hoist } = context;
return {
...parsed,
meta: {
...parsed.meta,
// Attach information collected to the AST
[AST_METADATA]: {
functions,
globals: scopes[0],
types,
userTypes,
statics,
},
},
params: [...parsed.params, ...hoist],
};
}
export default semantics;
|