2022-08-28 21:12:25 +02:00
2023-03-11 14:24:02 +01:00
import path from "path"
2022-09-09 20:18:51 +02:00
import stream from "stream"
2023-04-12 21:16:48 +02:00
import { InspectOptions } from "util" ;
2022-09-09 20:18:51 +02:00
2023-03-19 17:03:54 +01:00
export const isDebug = process . env [ 'NODE_ENV' ] === 'development' ;
2023-04-12 21:16:48 +02:00
export const toStringTag = Symbol . for ( 'nodejs.util.inspect.custom' ) ;
2023-03-19 17:03:54 +01:00
2023-04-12 21:16:48 +02:00
export type InspectFn = ( value : any , options : InspectOptions ) = > string ;
export function isIterable ( value : any ) : value is Iterable < any > {
if ( value === undefined || value === null ) {
return false ;
}
return typeof ( value [ Symbol . iterator ] ) === 'function' ;
}
2023-03-16 21:50:15 +01:00
2023-03-11 14:24:02 +01:00
export function first < T > ( iter : Iterator < T > ) : T | undefined {
return iter . next ( ) . value ;
}
export function last < T > ( iter : Iterator < T > ) : T | undefined {
let prevValue ;
for ( ; ; ) {
const { done , value } = iter . next ( ) ;
if ( done ) {
return prevValue ;
}
prevValue = value ;
}
}
export function stripExtension ( filepath : string ) : string {
const basename = path . basename ( filepath ) ;
const i = basename . lastIndexOf ( '.' ) ;
if ( i === - 1 ) {
return filepath ;
}
return path . join ( path . dirname ( filepath ) , basename . substring ( 0 , i ) ) ;
}
2022-09-09 20:18:51 +02:00
export class IndentWriter {
private atBlankLine = true ;
private indentLevel = 0 ;
public constructor (
private output : stream.Writable ,
private indentation = ' ' ,
) {
}
public write ( text : string ) : void {
for ( const ch of text ) {
if ( ch === '\n' ) {
this . atBlankLine = true ;
} else if ( ! /[\t ]/ . test ( ch ) && this . atBlankLine ) {
this . output . write ( this . indentation . repeat ( this . indentLevel ) ) ;
this . atBlankLine = false ;
}
this . output . write ( ch ) ;
}
}
public indent ( ) : void {
this . indentLevel ++ ;
}
public dedent ( ) : void {
this . indentLevel -- ;
}
}
2023-03-16 21:50:15 +01:00
const GITHUB_ISSUE_URL = 'https://github.com/boltlang/bolt/issues/'
2022-08-29 16:17:55 +02:00
export function assert ( test : boolean ) : asserts test {
if ( ! test ) {
2023-03-16 21:50:15 +01:00
throw new Error ( ` Assertion failed. See the stack trace for more information. You are invited to search this issue on GitHub or to create a new one at ${ GITHUB_ISSUE_URL } . ` ) ;
}
}
export function implementationLimitation ( test : boolean ) : asserts test {
if ( ! test ) {
throw new Error ( ` We encountered a limitation to the implementation of this compiler. You are invited to search this issue on GitHub or to create a new one at ${ GITHUB_ISSUE_URL } . ` ) ;
2022-08-29 16:17:55 +02:00
}
}
2023-04-12 21:16:48 +02:00
export function unreachable ( ) : never {
throw new Error ( ` Code that should never be executed was reached during operation. ` ) ;
}
2023-03-11 14:24:02 +01:00
export function assertNever ( value : never ) : never {
console . error ( value ) ;
throw new Error ( ` Assertion failed. See the stack trace for more information. ` ) ;
}
2022-08-31 13:29:56 +02:00
export function countDigits ( x : number , base : number = 10 ) {
return x === 0 ? 1 : Math.ceil ( Math . log ( x + 1 ) / Math . log ( base ) )
}
2022-09-06 15:13:07 +02:00
export function isEmpty < T > ( iter : Iterable < T > | Iterator < T > ) : boolean {
if ( ( iter as any ) [ Symbol . iterator ] !== undefined ) {
iter = ( iter as any ) [ Symbol . iterator ] ( ) ;
}
return ! ! ( iter as Iterator < T > ) . next ( ) . done ;
}
2022-08-29 16:17:55 +02:00
export type JSONValue = null | boolean | number | string | JSONArray | JSONObject
export type JSONArray = Array < JSONValue > ;
export type JSONObject = { [ key : string ] : JSONValue } ;
2022-08-28 21:12:25 +02:00
export class MultiDict < K , V > {
private mapping = new Map < K , V [ ] > ( ) ;
public constructor ( iterable? : Iterable < [ K , V ] > ) {
if ( iterable ) {
for ( const [ key , value ] of iterable ) {
this . add ( key , value ) ;
}
}
}
public get ( key : K ) : Iterable < V > {
return this . mapping . get ( key ) ? ? [ ] ;
}
public add ( key : K , value : V ) : void {
const values = this . mapping . get ( key ) ;
if ( values ) {
values . push ( value ) ;
} else {
this . mapping . set ( key , [ value ] )
}
}
public * [ Symbol . iterator ] ( ) : Iterator < [ K , V ] > {
for ( const [ key , values ] of this . mapping ) {
for ( const value of values ) {
yield [ key , value ] ;
}
}
}
}
export interface Stream < T > {
get ( ) : T ;
peek ( offset? : number ) : T ;
}
export abstract class BufferedStream < T > {
private buffer : Array < T > = [ ] ;
public abstract read ( ) : T ;
public get ( ) : T {
if ( this . buffer . length > 0 ) {
return this . buffer . shift ( ) ! ;
}
return this . read ( ) ;
}
public peek ( offset = 1 ) : T {
while ( this . buffer . length < offset ) {
this . buffer . push ( this . read ( ) ) ;
}
return this . buffer [ offset - 1 ] ;
}
}
2022-09-15 11:49:53 +02:00
export class MultiMap < K , V > {
private mapping = new Map < K , V [ ] > ( ) ;
public get ( key : K ) : V [ ] {
return this . mapping . get ( key ) ? ? [ ] ;
}
public add ( key : K , value : V ) : void {
let elements = this . mapping . get ( key ) ;
if ( elements === undefined ) {
elements = [ ] ;
this . mapping . set ( key , elements ) ;
}
elements . push ( value ) ;
}
public has ( key : K , value? : V ) : boolean {
if ( value === undefined ) {
return this . mapping . has ( key ) ;
}
const elements = this . mapping . get ( key ) ;
if ( elements === undefined ) {
return false ;
}
return elements . indexOf ( value ) !== - 1 ;
}
public keys ( ) : Iterable < K > {
return this . mapping . keys ( ) ;
}
public * values ( ) : Iterable < V > {
for ( const elements of this . mapping . values ( ) ) {
yield * elements ;
}
}
2023-04-12 21:16:48 +02:00
public * [ Symbol . iterator ] ( ) : Iterator < [ K , V ] > {
2022-09-15 11:49:53 +02:00
for ( const [ key , elements ] of this . mapping ) {
for ( const value of elements ) {
yield [ key , value ] ;
}
}
}
public delete ( key : K , value? : V ) : number {
const elements = this . mapping . get ( key ) ;
if ( elements === undefined ) {
return 0 ;
}
if ( value === undefined ) {
this . mapping . delete ( key ) ;
return elements . length ;
}
const i = elements . indexOf ( value ) ;
if ( i !== - 1 ) {
elements . splice ( i , 1 ) ;
if ( elements . length === 0 ) {
this . mapping . delete ( key ) ;
}
return 1 ;
}
return 0 ;
}
}
2023-04-14 19:57:59 +02:00
export const nonenumerable : {
( target : any , name : string ) : void ;
( target : any , name : string , desc : PropertyDescriptor ) : PropertyDescriptor ;
} = ( target : any , name : string , desc? : any ) = > {
if ( desc ) {
desc . enumerable = false ;
return desc ;
}
Object . defineProperty ( target , name , {
set ( value ) {
Object . defineProperty ( this , name , {
value , writable : true , configurable : true ,
} ) ;
} ,
configurable : true ,
} ) ;
} ;