Skip to content

Commit d7fe7c7

Browse files
committed
Add parse options to JavaScript's parsePrism function
1 parent 5f46d36 commit d7fe7c7

File tree

2 files changed

+142
-3
lines changed

2 files changed

+142
-3
lines changed

javascript/src/parsePrism.js

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,167 @@
11
import { ParseResult, deserialize } from "./deserialize.js";
2+
import os from "os";
23

34
/**
45
* Parse the given source code.
56
*
67
* @param {WebAssembly.Exports} prism
78
* @param {string} source
9+
* @param {Object} options
810
* @returns {ParseResult}
911
*/
10-
export function parsePrism(prism, source) {
12+
export function parsePrism(prism, source, options = {}) {
1113
const sourceArray = new TextEncoder().encode(source);
1214
const sourcePointer = prism.calloc(1, sourceArray.length);
1315

16+
const packedOptions = dumpOptions(options);
17+
const optionsPointer = prism.calloc(1, packedOptions.length);
18+
1419
const bufferPointer = prism.calloc(prism.pm_buffer_sizeof(), 1);
1520
prism.pm_buffer_init(bufferPointer);
1621

1722
const sourceView = new Uint8Array(prism.memory.buffer, sourcePointer, sourceArray.length);
1823
sourceView.set(sourceArray);
1924

20-
prism.pm_serialize_parse(bufferPointer, sourcePointer, sourceArray.length);
25+
const optionsView = new Uint8Array(prism.memory.buffer, optionsPointer, packedOptions.length);
26+
optionsView.set(packedOptions);
27+
28+
prism.pm_serialize_parse(bufferPointer, sourcePointer, sourceArray.length, optionsPointer);
2129
const serializedView = new Uint8Array(prism.memory.buffer, prism.pm_buffer_value(bufferPointer), prism.pm_buffer_length(bufferPointer));
2230
const result = deserialize(sourceArray, serializedView);
2331

2432
prism.pm_buffer_free(bufferPointer);
2533
prism.free(sourcePointer);
2634
prism.free(bufferPointer);
35+
prism.free(optionsPointer);
2736
return result;
2837
}
38+
39+
// Converts the given options into a serialized options string.
40+
function dumpOptions(options) {
41+
const values = [];
42+
const template = [];
43+
const encoder = new TextEncoder();
44+
45+
template.push("L")
46+
if (options.filepath) {
47+
const filepath = encoder.encode(options.filepath);
48+
values.push(filepath.length);
49+
values.push(filepath);
50+
template.push("A");
51+
} else {
52+
values.push(0);
53+
}
54+
55+
template.push("l");
56+
values.push(options.line || 1);
57+
58+
template.push("L");
59+
if (options.encoding) {
60+
const encoding = encoder.encode(options.encoding);
61+
values.push(encoding.length);
62+
values.push(encoding);
63+
template.push("A");
64+
} else {
65+
values.push(0);
66+
}
67+
68+
template.push("C");
69+
values.push(options.frozen_string_literal === undefined ? 0 : 1);
70+
71+
template.push("C");
72+
values.push(options.verbose === undefined ? 0 : 1);
73+
74+
template.push("C");
75+
if (!options.version || options.version === "latest") {
76+
values.push(0);
77+
} else if (options.version === "3.3.0") {
78+
values.push(1);
79+
} else {
80+
throw new Error(`Unsupported version '${options.version}' in compiler options`);
81+
}
82+
83+
template.push("L");
84+
if (options.scopes) {
85+
const scopes = options.scopes;
86+
values.push(scopes.length);
87+
88+
for (const scope of scopes) {
89+
template.push("L");
90+
values.push(scope.length);
91+
92+
for (const local of scope) {
93+
const name = local.name;
94+
template.push("L");
95+
values.push(name.length);
96+
97+
template.push("A")
98+
values.push(encoder.encode(name));
99+
}
100+
}
101+
} else {
102+
values.push(0);
103+
}
104+
105+
return pack(values, template);
106+
}
107+
108+
function totalSizeOf(values, template) {
109+
let size = 0;
110+
111+
for (let i = 0; i < values.length; i ++) {
112+
size += sizeOf(values, template, i);
113+
}
114+
115+
return size;
116+
}
117+
118+
function sizeOf(values, template, index) {
119+
switch (template[index]) {
120+
// arbitrary binary string
121+
case "A":
122+
return values[index].length;
123+
124+
// l: signed 32-bit integer, L: unsigned 32-bit integer
125+
case "l":
126+
case "L":
127+
return 4;
128+
129+
// 8-bit unsigned integer
130+
case "C":
131+
return 1;
132+
}
133+
}
134+
135+
function pack(values, template) {
136+
const littleEndian = os.endianness() === "LE";
137+
const buffer = new ArrayBuffer(totalSizeOf(values, template));
138+
const data_view = new DataView(buffer);
139+
let offset = 0;
140+
141+
for (let i = 0; i < values.length; i ++) {
142+
switch (template[i]) {
143+
case "A":
144+
for (let c = 0; c < values[i].length; c ++) {
145+
data_view.setUint8(offset + c, values[i][c]);
146+
}
147+
148+
break;
149+
150+
case "l":
151+
data_view.setInt32(offset, values[i], littleEndian);
152+
break;
153+
154+
case "L":
155+
data_view.setUint32(offset, values[i], littleEndian);
156+
break;
157+
158+
case "C":
159+
data_view.setUint8(offset, values[i], littleEndian);
160+
break;
161+
}
162+
163+
offset += sizeOf(values, template, i);
164+
}
165+
166+
return new Uint8Array(buffer);
167+
}

lib/prism/ffi.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def dump_options(options)
296296
values << 0
297297
end
298298

299-
template << "L"
299+
template << "l"
300300
values << options.fetch(:line, 1)
301301

302302
template << "L"

0 commit comments

Comments
 (0)