368 lines
10 KiB
JavaScript
368 lines
10 KiB
JavaScript
import * as $dict from "../gleam_stdlib/gleam/dict.mjs";
|
|
import * as $stdlib$dict from "../gleam_stdlib/gleam/dict.mjs";
|
|
import * as $int from "../gleam_stdlib/gleam/int.mjs";
|
|
import * as $list from "../gleam_stdlib/gleam/list.mjs";
|
|
import * as $result from "../gleam_stdlib/gleam/result.mjs";
|
|
import * as $string from "../gleam_stdlib/gleam/string.mjs";
|
|
import * as $definition from "./definition.mjs";
|
|
import {
|
|
Ok,
|
|
toList,
|
|
Empty as $Empty,
|
|
NonEmpty as $NonEmpty,
|
|
CustomType as $CustomType,
|
|
bitArraySlice,
|
|
bitArraySliceToInt,
|
|
BitArray as $BitArray,
|
|
List as $List,
|
|
UtfCodepoint as $UtfCodepoint,
|
|
} from "./gleam.mjs";
|
|
import * as $mode from "./mode.mjs";
|
|
|
|
function whatsthat(mod) {
|
|
let modes = $dict.from_list(
|
|
toList([
|
|
[
|
|
"!",
|
|
[
|
|
new $mode.Marking(),
|
|
(var0) => { return new $definition.Marking(var0); },
|
|
],
|
|
],
|
|
[
|
|
"@",
|
|
[new $mode.Truth(), (var0) => { return new $definition.Truth(var0); }],
|
|
],
|
|
[
|
|
">",
|
|
[new $mode.Quote(), (var0) => { return new $definition.Quote(var0); }],
|
|
],
|
|
[
|
|
"#",
|
|
[
|
|
new $mode.Hirachy(),
|
|
(var0) => { return new $definition.Hirachy(var0); },
|
|
],
|
|
],
|
|
]),
|
|
);
|
|
let $ = $string.pop_grapheme(mod);
|
|
if ($ instanceof Ok) {
|
|
let prefix = $[0][0];
|
|
let rest = $[0][1];
|
|
let $1 = $dict.get(modes, prefix);
|
|
if ($1 instanceof Ok) {
|
|
let m = $1[0][0];
|
|
let make_definition = $1[0][1];
|
|
if (rest === "-") {
|
|
return new $definition.Pop(m);
|
|
} else {
|
|
let _block;
|
|
let _pipe = $int.parse(rest);
|
|
_block = $result.unwrap(_pipe, 1);
|
|
let value = _block;
|
|
return new $definition.Push(make_definition(value));
|
|
}
|
|
} else {
|
|
return new $definition.Text(mod);
|
|
}
|
|
} else {
|
|
return new $definition.Text(mod);
|
|
}
|
|
}
|
|
|
|
function parse(modificators) {
|
|
let _pipe = modificators;
|
|
let _pipe$1 = $string.replace(_pipe, "#", " |#");
|
|
let _pipe$2 = $string.replace(_pipe$1, "@", " |@");
|
|
let _pipe$3 = $string.replace(_pipe$2, "!", " |!");
|
|
let _pipe$4 = $string.replace(_pipe$3, ">", " |>");
|
|
let _pipe$5 = $string.split(_pipe$4, " |");
|
|
let _pipe$6 = $list.flat_map(
|
|
_pipe$5,
|
|
(block) => {
|
|
let $ = (($string.starts_with(block, "#") || $string.starts_with(
|
|
block,
|
|
"@",
|
|
)) || $string.starts_with(block, "!")) || $string.starts_with(block, ">");
|
|
if ($) {
|
|
let $1 = $string.split_once(block, " ");
|
|
if ($1 instanceof Ok) {
|
|
let tag = $1[0][0];
|
|
let rest = $1[0][1];
|
|
return toList([tag, rest]);
|
|
} else {
|
|
return toList([block]);
|
|
}
|
|
} else {
|
|
return toList([block]);
|
|
}
|
|
},
|
|
);
|
|
let _pipe$7 = $list.map(_pipe$6, $string.trim);
|
|
return $list.filter(_pipe$7, (x) => { return x !== ""; });
|
|
}
|
|
|
|
export function handler(line) {
|
|
let result = parse(line);
|
|
let result$1 = echo(
|
|
$list.map(result, whatsthat),
|
|
undefined,
|
|
"src/parser.gleam",
|
|
10,
|
|
);
|
|
return new $definition.Line(result$1);
|
|
}
|
|
|
|
function echo(value, message, file, line) {
|
|
const grey = "\u001b[90m";
|
|
const reset_color = "\u001b[39m";
|
|
const file_line = `${file}:${line}`;
|
|
const inspector = new Echo$Inspector();
|
|
const string_value = inspector.inspect(value);
|
|
const string_message = message === undefined ? "" : " " + message;
|
|
|
|
if (globalThis.process?.stderr?.write) {
|
|
// If we're in Node.js, use `stderr`
|
|
const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`;
|
|
globalThis.process.stderr.write(string);
|
|
} else if (globalThis.Deno) {
|
|
// If we're in Deno, use `stderr`
|
|
const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`;
|
|
globalThis.Deno.stderr.writeSync(new TextEncoder().encode(string));
|
|
} else {
|
|
// Otherwise, use `console.log`
|
|
// The browser's console.log doesn't support ansi escape codes
|
|
const string = `${file_line}${string_message}\n${string_value}`;
|
|
globalThis.console.log(string);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
class Echo$Inspector {
|
|
#references = new globalThis.Set();
|
|
|
|
#isDict(value) {
|
|
try {
|
|
// We can only check if an object is a stdlib Dict if it is one of the
|
|
// project's dependencies.
|
|
// We import the public gleam/dict module, so to check if something is a
|
|
// dict we compare the `constructor` field on the object with that of a
|
|
// new dict.
|
|
const empty_dict = $stdlib$dict.new$();
|
|
const dict_class = empty_dict.constructor;
|
|
return value instanceof dict_class;
|
|
} catch {
|
|
// If stdlib is not one of the project's dependencies then `$stdlib$dict`
|
|
// will not have been imported and the check will throw an exception meaning
|
|
// we can't check if something is actually a `Dict`.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#float(float) {
|
|
const string = float.toString().replace("+", "");
|
|
if (string.indexOf(".") >= 0) {
|
|
return string;
|
|
} else {
|
|
const index = string.indexOf("e");
|
|
if (index >= 0) {
|
|
return string.slice(0, index) + ".0" + string.slice(index);
|
|
} else {
|
|
return string + ".0";
|
|
}
|
|
}
|
|
}
|
|
|
|
inspect(v) {
|
|
const t = typeof v;
|
|
if (v === true) return "True";
|
|
if (v === false) return "False";
|
|
if (v === null) return "//js(null)";
|
|
if (v === undefined) return "Nil";
|
|
if (t === "string") return this.#string(v);
|
|
if (t === "bigint" || globalThis.Number.isInteger(v)) return v.toString();
|
|
if (t === "number") return this.#float(v);
|
|
if (v instanceof $UtfCodepoint) return this.#utfCodepoint(v);
|
|
if (v instanceof $BitArray) return this.#bit_array(v);
|
|
if (v instanceof globalThis.RegExp) return `//js(${v})`;
|
|
if (v instanceof globalThis.Date) return `//js(Date("${v.toISOString()}"))`;
|
|
if (v instanceof globalThis.Error) return `//js(${v.toString()})`;
|
|
if (v instanceof globalThis.Function) {
|
|
const args = [];
|
|
for (const i of globalThis.Array(v.length).keys())
|
|
args.push(globalThis.String.fromCharCode(i + 97));
|
|
return `//fn(${args.join(", ")}) { ... }`;
|
|
}
|
|
|
|
if (this.#references.size === this.#references.add(v).size) {
|
|
return "//js(circular reference)";
|
|
}
|
|
|
|
let printed;
|
|
if (globalThis.Array.isArray(v)) {
|
|
printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`;
|
|
} else if (v instanceof $List) {
|
|
printed = this.#list(v);
|
|
} else if (v instanceof $CustomType) {
|
|
printed = this.#customType(v);
|
|
} else if (this.#isDict(v)) {
|
|
printed = this.#dict(v);
|
|
} else if (v instanceof Set) {
|
|
return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`;
|
|
} else {
|
|
printed = this.#object(v);
|
|
}
|
|
this.#references.delete(v);
|
|
return printed;
|
|
}
|
|
|
|
#object(v) {
|
|
const name =
|
|
globalThis.Object.getPrototypeOf(v)?.constructor?.name || "Object";
|
|
const props = [];
|
|
for (const k of globalThis.Object.keys(v)) {
|
|
props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`);
|
|
}
|
|
const body = props.length ? " " + props.join(", ") + " " : "";
|
|
const head = name === "Object" ? "" : name + " ";
|
|
return `//js(${head}{${body}})`;
|
|
}
|
|
|
|
#dict(map) {
|
|
let body = "dict.from_list([";
|
|
let first = true;
|
|
|
|
let key_value_pairs = $stdlib$dict.fold(map, [], (pairs, key, value) => {
|
|
pairs.push([key, value]);
|
|
return pairs;
|
|
});
|
|
|
|
key_value_pairs.sort();
|
|
key_value_pairs.forEach(([key, value]) => {
|
|
if (!first) body = body + ", ";
|
|
body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")";
|
|
first = false;
|
|
});
|
|
return body + "])";
|
|
}
|
|
|
|
#customType(record) {
|
|
const props = globalThis.Object.keys(record)
|
|
.map((label) => {
|
|
const value = this.inspect(record[label]);
|
|
return isNaN(parseInt(label)) ? `${label}: ${value}` : value;
|
|
})
|
|
.join(", ");
|
|
return props
|
|
? `${record.constructor.name}(${props})`
|
|
: record.constructor.name;
|
|
}
|
|
|
|
#list(list) {
|
|
if (list instanceof $Empty) {
|
|
return "[]";
|
|
}
|
|
|
|
let char_out = 'charlist.from_string("';
|
|
let list_out = "[";
|
|
|
|
let current = list;
|
|
while (current instanceof $NonEmpty) {
|
|
let element = current.head;
|
|
current = current.tail;
|
|
|
|
if (list_out !== "[") {
|
|
list_out += ", ";
|
|
}
|
|
list_out += this.inspect(element);
|
|
|
|
if (char_out) {
|
|
if (
|
|
globalThis.Number.isInteger(element) &&
|
|
element >= 32 &&
|
|
element <= 126
|
|
) {
|
|
char_out += globalThis.String.fromCharCode(element);
|
|
} else {
|
|
char_out = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (char_out) {
|
|
return char_out + '")';
|
|
} else {
|
|
return list_out + "]";
|
|
}
|
|
}
|
|
|
|
#string(str) {
|
|
let new_str = '"';
|
|
for (let i = 0; i < str.length; i++) {
|
|
const char = str[i];
|
|
switch (char) {
|
|
case "\n":
|
|
new_str += "\\n";
|
|
break;
|
|
case "\r":
|
|
new_str += "\\r";
|
|
break;
|
|
case "\t":
|
|
new_str += "\\t";
|
|
break;
|
|
case "\f":
|
|
new_str += "\\f";
|
|
break;
|
|
case "\\":
|
|
new_str += "\\\\";
|
|
break;
|
|
case '"':
|
|
new_str += '\\"';
|
|
break;
|
|
default:
|
|
if (char < " " || (char > "~" && char < "\u{00A0}")) {
|
|
new_str +=
|
|
"\\u{" +
|
|
char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") +
|
|
"}";
|
|
} else {
|
|
new_str += char;
|
|
}
|
|
}
|
|
}
|
|
new_str += '"';
|
|
return new_str;
|
|
}
|
|
|
|
#utfCodepoint(codepoint) {
|
|
return `//utfcodepoint(${globalThis.String.fromCodePoint(codepoint.value)})`;
|
|
}
|
|
|
|
#bit_array(bits) {
|
|
if (bits.bitSize === 0) {
|
|
return "<<>>";
|
|
}
|
|
|
|
let acc = "<<";
|
|
|
|
for (let i = 0; i < bits.byteSize - 1; i++) {
|
|
acc += bits.byteAt(i).toString();
|
|
acc += ", ";
|
|
}
|
|
|
|
if (bits.byteSize * 8 === bits.bitSize) {
|
|
acc += bits.byteAt(bits.byteSize - 1).toString();
|
|
} else {
|
|
const trailingBitsCount = bits.bitSize % 8;
|
|
acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount);
|
|
acc += `:size(${trailingBitsCount})`;
|
|
}
|
|
|
|
acc += ">>";
|
|
return acc;
|
|
}
|
|
}
|
|
|