diff --git a/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts b/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts index 2dec54fa..034e6fcd 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts @@ -14,6 +14,7 @@ export class ArrayLikeDeconstr readonly rest: Identifier | undefined, readonly type: PebbleAstType | undefined, // just for the type checker, ususally this is inferred readonly initExpr: PebbleExpr | undefined, + public flags: number, readonly range: SourceRange, ) {} } \ No newline at end of file diff --git a/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts index 49696dfe..a9197d56 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts @@ -1,3 +1,4 @@ +import { CommonFlags } from "../../../../../common"; import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; @@ -15,6 +16,7 @@ export class NamedDeconstructVarDecl readonly rest: Identifier | undefined, readonly type: PebbleAstType | undefined, // can be undefined when use ad function parameter readonly initExpr: PebbleExpr | undefined, // can be undefined when use ad function parameter + public flags: CommonFlags, readonly range: SourceRange, ) {} @@ -31,6 +33,7 @@ export class NamedDeconstructVarDecl unnamed.rest, unnamed.type, unnamed.initExpr, + unnamed.flags, range ); } diff --git a/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts index f27829ce..f4ff8362 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts @@ -1,3 +1,4 @@ +import { CommonFlags } from "../../../../../common"; import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; @@ -12,15 +13,17 @@ export class SimpleVarDecl readonly name: Identifier, readonly type: PebbleAstType | undefined, readonly initExpr: PebbleExpr | undefined, + public flags: CommonFlags, readonly range: SourceRange, ) {} - static onlyIdentifier( identifier: Identifier ) + static onlyIdentifier( identifier: Identifier, flags: CommonFlags ): SimpleVarDecl { return new SimpleVarDecl( identifier, undefined, undefined, + flags, identifier.range ); } diff --git a/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts index 06370990..02ecf403 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts @@ -1,3 +1,4 @@ +import { CommonFlags } from "../../../../../common"; import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; @@ -21,6 +22,7 @@ export class SingleDeconstructVarDecl readonly rest: Identifier | undefined, readonly type: PebbleAstType | undefined, readonly initExpr: PebbleExpr | undefined, + public flags: CommonFlags, readonly range: SourceRange ) {} } \ No newline at end of file diff --git a/src/common.ts b/src/common.ts index 48006ffa..918dc785 100644 --- a/src/common.ts +++ b/src/common.ts @@ -86,7 +86,11 @@ export enum CommonFlags { Object.freeze(CommonFlags); -export const WIN = globalThis.process && globalThis.process.platform === "win32"; +let hasGlobalThis: boolean; +try { + hasGlobalThis = typeof globalThis !== "undefined" +} catch { hasGlobalThis = false; } +export const WIN = hasGlobalThis && globalThis.process && globalThis.process.platform === "win32"; export const EOL = WIN ? "\r\n" : "\n"; export const SEP = WIN ? "\\" : "/"; diff --git a/src/compiler/AstCompiler/AstCompiler.ts b/src/compiler/AstCompiler/AstCompiler.ts index 0b9790ab..0d6b5e05 100644 --- a/src/compiler/AstCompiler/AstCompiler.ts +++ b/src/compiler/AstCompiler/AstCompiler.ts @@ -11,9 +11,7 @@ import { CompilerOptions } from "../../IR/toUPLC/CompilerOptions"; import { Parser } from "../../parser/Parser"; import { CompilerIoApi, createMemoryCompilerIoApi } from "../io/CompilerIoApi"; import { IPebbleCompiler } from "../IPebbleCompiler"; -import { mangleInternalPath } from "../path/mangleInternalPath"; -import { Path, removeSingleDotDirsFromPath, resolveAsRootPath } from "../path/path"; -import { DependencyGraph } from "./DependencyGrapth"; +import { getInternalPath, Path, resolveProjAbsolutePath } from "../path/path"; class ResolveStackNode { constructor( @@ -94,8 +92,8 @@ export class AstCompiler extends DiagnosticEmitter isEntry: boolean = true ) { - const internalPath = mangleInternalPath( removeSingleDotDirsFromPath( path ) ); - srcText = srcText ?? await this.io.readFile( path, this.rootPath ); + const internalPath = getInternalPath( path ); + srcText = srcText ?? await this.io.readFile( internalPath, this.rootPath ); if( !srcText ) { this.error( @@ -125,7 +123,7 @@ export class AstCompiler extends DiagnosticEmitter if(!( src instanceof Source )) { src = src.toString(); - src = mangleInternalPath( removeSingleDotDirsFromPath( src ) ); + src = getInternalPath( src ); src = (await this.sourceFromInternalPath( src ))!; if( !src ) return this.diagnostics; } @@ -194,7 +192,7 @@ export class AstCompiler extends DiagnosticEmitter const importStmt = req.dependent.statements.find( stmt => { if( !isImportStmtLike( stmt ) ) return false; - const asRootPath = resolveAsRootPath( + const asRootPath = resolveProjAbsolutePath( stmt.fromPath.string, req.dependent.internalPath ); @@ -203,9 +201,7 @@ export class AstCompiler extends DiagnosticEmitter return asRootPath === prevPath }) as ImportStmtLike | undefined; - prevPath = mangleInternalPath( - removeSingleDotDirsFromPath( req.dependent.internalPath ) - ); + prevPath = getInternalPath( req.dependent.internalPath ); if( !importStmt ) { this.error( @@ -271,7 +267,7 @@ export class AstCompiler extends DiagnosticEmitter { return stmts .map( imp => { - const internalPath = resolveAsRootPath( + const internalPath = resolveProjAbsolutePath( imp.fromPath.string, requestingPath ); @@ -283,7 +279,7 @@ export class AstCompiler extends DiagnosticEmitter ); return ""; } - return mangleInternalPath( internalPath ); + return getInternalPath( internalPath ); }) .filter( path => path !== "" ); } diff --git a/src/compiler/AstCompiler/desugar.ts b/src/compiler/AstCompiler/desugar.ts new file mode 100644 index 00000000..0ad6601f --- /dev/null +++ b/src/compiler/AstCompiler/desugar.ts @@ -0,0 +1,28 @@ +import { PebbleStmt } from "../../ast/nodes/statements/PebbleStmt"; +import { VarStmt } from "../../ast/nodes/statements/VarStmt"; + +export function desugarVarStatements( stmts: PebbleStmt[] ): void +{ + for( let i = 0; i < stmts.length; ++i ) + { + const stmt = stmts[ i ]; + + if( stmt instanceof VarStmt ) + { + if( stmt.declarations.length > 1 ) + { + const newStmts: PebbleStmt[] = []; + + for( const decl of stmt.declarations ) + { + newStmts.push( new VarStmt( [ decl ], decl.range ) ); + } + + stmts.splice( i, 1, ...newStmts ); + i--; + continue; // desugar single declarations + } + if( stmt.declarations.length <= 0 ) throw new Error( "Empty VarStmt" ); + } + } +} \ No newline at end of file diff --git a/src/compiler/path/__tests__/compiler.path.test.ts b/src/compiler/path/__tests__/compiler.path.test.ts index 5c8ba907..cd5b9340 100644 --- a/src/compiler/path/__tests__/compiler.path.test.ts +++ b/src/compiler/path/__tests__/compiler.path.test.ts @@ -1,4 +1,4 @@ -import { removeSingleDotDirsFromPath, dirname, resolveAsRootPath } from "../path"; +import { removeSingleDotDirsFromPath, dirname, resolveProjAbsolutePath } from "../path"; describe("path", () => { @@ -61,12 +61,12 @@ describe("path", () => { }); - describe("resolveAsRootPath", () => { + describe("resolveProjAbsolutePath", () => { function testPath( inp: string, origin: string, out: string | undefined ) { test(inp + " from " + origin +" -> " + out, () => { - expect( resolveAsRootPath( inp, origin ) ).toEqual( out ); + expect( resolveProjAbsolutePath( inp, origin ) ).toEqual( out ); }); } diff --git a/src/compiler/path/mangleInternalPath.ts b/src/compiler/path/mangleInternalPath.ts deleted file mode 100644 index 2f1beabf..00000000 --- a/src/compiler/path/mangleInternalPath.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { extension } from "../../common"; - -export const fileExtension = ".pebble"; - -/** Mangles an external to an internal path. */ -export function mangleInternalPath(path: string): string { - if (path.endsWith( extension )) - path = path.substring( 0, path.length - extension.length ); - if (path.endsWith("/")) { - path += "index"; - } else if (path.endsWith(fileExtension)) { - path = path.substring(0, path.length - fileExtension.length); - } - return path; -} \ No newline at end of file diff --git a/src/compiler/path/path.ts b/src/compiler/path/path.ts index ae8c2406..6a13f8c4 100644 --- a/src/compiler/path/path.ts +++ b/src/compiler/path/path.ts @@ -8,6 +8,7 @@ import { } from "../../utils/text"; import { + extension, PATH_DELIMITER } from "../../common"; @@ -15,6 +16,22 @@ const separator = CharCode.Slash; export type Path = string; +export function getInternalPath( path: string ): string +{ + return mangleInternalPath( removeSingleDotDirsFromPath( path ) ); +} + +/** Mangles an external to an internal path. */ +export function mangleInternalPath(path: string): string { + if (path.endsWith( extension )) + path = path.substring( 0, path.length - extension.length ); + if (path.endsWith("/")) { + path += "index"; + } else if (path.endsWith(extension)) { + path = path.substring(0, path.length - extension.length); + } + return path; +} /** * Normalizes the specified path, removing interior placeholders. @@ -112,7 +129,7 @@ export function removeSingleDotDirsFromPath(path: string): string { } /** Resolves the specified path relative to the specified origin. */ -export function resolveAsRootPath( toResolve: string, fromPath: string ): string | undefined +export function resolveProjAbsolutePath( toResolve: string, fromPath: string ): string | undefined { const fromDirname = dirname( fromPath ) toResolve = removeSingleDotDirsFromPath( toResolve ); diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 1511340b..ecf85072 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -75,8 +75,7 @@ import { makeBinaryExpr } from "../ast/nodes/expr/binary/BinaryExpr"; import { AssignmentStmt, isAssignmentStmt, isAssignmentToken, makeAssignmentStmt } from "../ast/nodes/statements/AssignmentStmt"; import { ExprStmt } from "../ast/nodes/statements/ExprStmt"; import { PebbleAstType } from "../ast/nodes/types/PebbleAstType"; -import { mangleInternalPath } from "../compiler/path/mangleInternalPath"; -import { removeSingleDotDirsFromPath } from "../compiler/path/path"; +import { getInternalPath, removeSingleDotDirsFromPath } from "../compiler/path/path"; export class Parser extends DiagnosticEmitter { @@ -96,8 +95,7 @@ export class Parser extends DiagnosticEmitter isEntry: boolean = false ): [ Source, DiagnosticMessage[] ] { - const normalizedPath = removeSingleDotDirsFromPath( path ); - const internalPath = mangleInternalPath(normalizedPath); + const internalPath = getInternalPath( path ); const kind = ( isEntry @@ -179,7 +177,7 @@ export class Parser extends DiagnosticEmitter case Token.Let: case Token.Const: { tn.next(); // skip `const` | `let` | `var` - flags |= first === Token.Var ? CommonFlags.None : CommonFlags.Const; + flags |= first === Token.Const ? CommonFlags.Const : CommonFlags.None; if( tn.skip( Token.Enum ) ) { @@ -664,7 +662,7 @@ export class Parser extends DiagnosticEmitter } parseStruct( - flags = CommonFlags.None, + flags = CommonFlags.Const, startPos?: number ): StructDecl | undefined { @@ -739,7 +737,7 @@ export class Parser extends DiagnosticEmitter constrIdentifier = undefined; isSingleConstrShortcut = true; tn.reset( structDefBodyOpenState ); - const fields = this.parseStructConstrFields(); + const fields = this.parseStructConstrFields( flags ); if( !fields ) return undefined; if( !tn.skip( Token.CloseBrace ) ) return this.error( @@ -761,7 +759,7 @@ export class Parser extends DiagnosticEmitter ); } - const constrFields = this.parseStructConstrFields(); + const constrFields = this.parseStructConstrFields( flags ); if( !Array.isArray( constrFields ) ) return undefined; const constrs = [ @@ -790,7 +788,7 @@ export class Parser extends DiagnosticEmitter tn.range(), "{" ); - const constrFields = this.parseStructConstrFields(); + const constrFields = this.parseStructConstrFields( flags ); if( !Array.isArray( constrFields ) ) return undefined; constrs.push( @@ -812,7 +810,7 @@ export class Parser extends DiagnosticEmitter ); } - parseStructConstrFields(): VarDecl[] | undefined + parseStructConstrFields( flags: CommonFlags ): VarDecl[] | undefined { const tn = this.tn; // at '{' @@ -821,7 +819,7 @@ export class Parser extends DiagnosticEmitter while( !tn.skip( Token.CloseBrace ) ) { - const field = this._parseVarDecl(); + const field = this._parseVarDecl( flags ); if( !field ) return undefined; if( !field.type ) @@ -1105,7 +1103,7 @@ export class Parser extends DiagnosticEmitter * an initializer MUST NOT be present NOR a type */ private _parseVarDecl( - flags: CommonFlags = CommonFlags.None, + flags: CommonFlags, ): VarDecl | undefined { const tn = this.tn; @@ -1117,7 +1115,7 @@ export class Parser extends DiagnosticEmitter tn.skip( Token.OpenBrace ) ) // ConstrName{ ... } || { ... } { - const unnamed = this.parseSingleDeconstructVarDecl(); + const unnamed = this.parseSingleDeconstructVarDecl( flags ); if( !unnamed ) return undefined; return renamedField instanceof Identifier ? // ConstrName{ ... } @@ -1135,7 +1133,7 @@ export class Parser extends DiagnosticEmitter ); return undefined; } - return this.parseArrayLikeDeconstr(); + return this.parseArrayLikeDeconstr( flags ); } else if( renamedField instanceof Identifier ) // renamed { @@ -1149,13 +1147,14 @@ export class Parser extends DiagnosticEmitter renamedField, explicitType, initializer, + flags, range ); } else return undefined; } - parseSingleDeconstructVarDecl(): SingleDeconstructVarDecl | undefined + parseSingleDeconstructVarDecl( flags: CommonFlags ): SingleDeconstructVarDecl | undefined { const tn = this.tn; @@ -1167,7 +1166,6 @@ export class Parser extends DiagnosticEmitter let rest: Identifier | undefined = undefined; let isRest = false; let startRange: SourceRange | undefined = undefined - let flags = CommonFlags.None; let explicitType: PebbleAstType | undefined = undefined; let initializer: PebbleExpr | undefined = undefined; while( !tn.skip( Token.CloseBrace ) ) @@ -1203,14 +1201,14 @@ export class Parser extends DiagnosticEmitter if( !tn.skip( Token.Colon ) ) // only field, with no colon (eg: { field, ... }) { - element = SimpleVarDecl.onlyIdentifier( fieldName ); + element = SimpleVarDecl.onlyIdentifier( fieldName, flags ); elements.set( fieldName.name, element ); tn.skip( Token.Comma ); // skip comma if present continue; // early continue to check for close brace or next field } // else ther is colon (eg: { field: ... }) - element = this._parseVarDecl(); + element = this._parseVarDecl( flags ); if( !element ) // field: ... what? { this.error( @@ -1248,11 +1246,12 @@ export class Parser extends DiagnosticEmitter rest, explicitType, initializer, + flags, SourceRange.join( initRange, tn.range() ) ); } - parseArrayLikeDeconstr(): ArrayLikeDeconstr | undefined + parseArrayLikeDeconstr( flags: CommonFlags ): ArrayLikeDeconstr | undefined { const tn = this.tn; @@ -1291,7 +1290,7 @@ export class Parser extends DiagnosticEmitter rest.range ); - const elem = this._parseVarDecl(); + const elem = this._parseVarDecl( flags ); if( !elem ) return undefined; if( elem.initExpr || elem.type ) @@ -1326,6 +1325,7 @@ export class Parser extends DiagnosticEmitter rest, explicitType, initializer, + flags, range ); } @@ -1983,12 +1983,7 @@ export class Parser extends DiagnosticEmitter return this.parseCommonFuncExpr( Identifier.anonymous(tn.range(startPos)), [ - new SimpleVarDecl( - identifier, - undefined, // var type - undefined, // var initializer - identifier.range - ) + SimpleVarDecl.onlyIdentifier( identifier, CommonFlags.None ) ], ArrowKind.Single, startPos @@ -2127,7 +2122,7 @@ export class Parser extends DiagnosticEmitter const startPos = tn.tokenPos; - const matcher = this._parseVarDecl(); + const matcher = this._parseVarDecl( CommonFlags.Const ); if( !matcher ) return undefined; if( matcher instanceof SimpleVarDecl ) noPatternCaseSeen = true; @@ -2243,7 +2238,7 @@ export class Parser extends DiagnosticEmitter while (!tn.skip(Token.CloseParen)) { - let param = this.parseParameter(); + let param = this.parseParameter( CommonFlags.Const ); if (!param) return undefined; parameters.push(param); @@ -2263,13 +2258,11 @@ export class Parser extends DiagnosticEmitter return parameters; } - parseParameter(): VarDecl | undefined + parseParameter( flags: CommonFlags ): VarDecl | undefined { const tn = this.tn; - // before: ('public' | 'private' | 'protected' | '...')? Identifier '?'? (':' Type)? ('=' PebbleExpr)? + // before: Identifier '?'? (':' Type)? ('=' PebbleExpr)? - let accessFlags: CommonFlags = CommonFlags.None; - if (tn.skip(Token.Dot_Dot_Dot)) { this.error( DiagnosticCode.A_parameter_property_cannot_be_declared_using_a_rest_parameter, @@ -2278,7 +2271,7 @@ export class Parser extends DiagnosticEmitter return undefined; } - return this._parseVarDecl( accessFlags ); + return this._parseVarDecl( flags ); } /** @@ -3063,7 +3056,7 @@ export class Parser extends DiagnosticEmitter ); // const startPos = tn.pos; - const pattern = this._parseVarDecl(); + const pattern = this._parseVarDecl( CommonFlags.Const ); if( !pattern ) return this.error(