diff --git a/src/ast/PebbleAst.ts b/src/ast/PebbleAst.ts index a65a55c8..43f9bd5a 100644 --- a/src/ast/PebbleAst.ts +++ b/src/ast/PebbleAst.ts @@ -2,7 +2,7 @@ import { isVarDecl, VarDecl } from "./nodes/statements/declarations/VarDecl/VarD import { Identifier } from "./nodes/common/Identifier"; import { isPebbleExpr, PebbleExpr } from "./nodes/expr/PebbleExpr"; import { isPebbleStmt, PebbleStmt } from "./nodes/statements/PebbleStmt"; -import { isPebbleType, PebbleType } from "./nodes/types/PebbleType"; +import { isPebbleAstType, PebbleAstType } from "./nodes/types/PebbleAstType"; import { isPebbleDecl, PebbleDecl } from "./nodes/statements/declarations/PebbleDecl"; @@ -10,7 +10,7 @@ export type PebbleAst = VarDecl | PebbleStmt | PebbleExpr - | PebbleType + | PebbleAstType | Identifier | PebbleDecl ; @@ -22,7 +22,7 @@ export function isPebbleAst( thing: any ): thing is PebbleAst || isVarDecl( thing ) || isPebbleStmt( thing ) || isPebbleExpr( thing ) - || isPebbleType( thing ) + || isPebbleAstType( thing ) || isPebbleDecl( thing ) ); } \ No newline at end of file diff --git a/src/ast/Source/Source.ts b/src/ast/Source/Source.ts index cb02f1bc..c6b3b747 100644 --- a/src/ast/Source/Source.ts +++ b/src/ast/Source/Source.ts @@ -1,6 +1,6 @@ import { LIBRARY_PREFIX, PATH_DELIMITER, LIBRARY_SUBST } from "../../common"; import { CharCode } from "../../utils/CharCode"; -import { mangleInternalPath } from "../../utils/mangleInternalPath"; +import { mangleInternalPath } from "../../compiler/path/mangleInternalPath"; import { PebbleStmt } from "../nodes/statements/PebbleStmt"; import { PebbleAst } from "../PebbleAst"; import { SourceRange } from "./SourceRange"; diff --git a/src/ast/nodes/expr/ElemAccessExpr.ts b/src/ast/nodes/expr/ElemAccessExpr.ts index 19241af0..2c8becdf 100644 --- a/src/ast/nodes/expr/ElemAccessExpr.ts +++ b/src/ast/nodes/expr/ElemAccessExpr.ts @@ -1,6 +1,6 @@ import { SourceRange } from "../../Source/SourceRange"; import { HasSourceRange } from "../HasSourceRange"; -import { PebbleType } from "../types/PebbleType"; +import { PebbleAstType } from "../types/PebbleAstType"; import { PebbleExpr } from "./PebbleExpr"; diff --git a/src/ast/nodes/expr/InstanceOfExpr.ts b/src/ast/nodes/expr/InstanceOfExpr.ts index 04a447b3..4f955cdb 100644 --- a/src/ast/nodes/expr/InstanceOfExpr.ts +++ b/src/ast/nodes/expr/InstanceOfExpr.ts @@ -1,6 +1,6 @@ import { SourceRange } from "../../Source/SourceRange"; import { HasSourceRange } from "../HasSourceRange"; -import { PebbleType } from "../types/PebbleType"; +import { PebbleAstType } from "../types/PebbleAstType"; import { PebbleExpr } from "./PebbleExpr"; @@ -9,7 +9,7 @@ export class InstanceOfExpr { constructor( readonly instanceExpr: PebbleExpr, - readonly ofType: PebbleType, + readonly ofType: PebbleAstType, readonly range: SourceRange ) {} } \ No newline at end of file diff --git a/src/ast/nodes/expr/NonNullExpr.ts b/src/ast/nodes/expr/NonNullExpr.ts index 500749b1..1aa1fcd6 100644 --- a/src/ast/nodes/expr/NonNullExpr.ts +++ b/src/ast/nodes/expr/NonNullExpr.ts @@ -1,6 +1,6 @@ import { SourceRange } from "../../Source/SourceRange"; import { HasSourceRange } from "../HasSourceRange"; -import { PebbleType } from "../types/PebbleType"; +import { PebbleAstType } from "../types/PebbleAstType"; import { PebbleExpr } from "./PebbleExpr"; diff --git a/src/ast/nodes/expr/TypeConversionExpr.ts b/src/ast/nodes/expr/TypeConversionExpr.ts index 8fb52e51..732cf7e6 100644 --- a/src/ast/nodes/expr/TypeConversionExpr.ts +++ b/src/ast/nodes/expr/TypeConversionExpr.ts @@ -1,6 +1,6 @@ import { SourceRange } from "../../Source/SourceRange"; import { HasSourceRange } from "../HasSourceRange"; -import { PebbleType } from "../types/PebbleType"; +import { PebbleAstType } from "../types/PebbleAstType"; import { PebbleExpr } from "./PebbleExpr"; @@ -9,7 +9,7 @@ export class TypeConversionExpr { constructor( readonly expr: PebbleExpr, - readonly asType: PebbleType, + readonly asType: PebbleAstType, readonly range: SourceRange = SourceRange.join( expr.range, asType.range ) ) {} } \ No newline at end of file diff --git a/src/ast/nodes/expr/functions/CallExpr.ts b/src/ast/nodes/expr/functions/CallExpr.ts index 65256343..75101f80 100644 --- a/src/ast/nodes/expr/functions/CallExpr.ts +++ b/src/ast/nodes/expr/functions/CallExpr.ts @@ -1,6 +1,6 @@ import { SourceRange } from "../../../Source/SourceRange"; import { HasSourceRange } from "../../HasSourceRange"; -import { PebbleType } from "../../types/PebbleType"; +import { PebbleAstType } from "../../types/PebbleAstType"; import { PebbleExpr } from "../PebbleExpr"; export class CallExpr implements HasSourceRange @@ -8,7 +8,7 @@ export class CallExpr implements HasSourceRange constructor( readonly func: PebbleExpr, /** if the function is generic */ - readonly genericTypeArgs: PebbleType[] | undefined, + readonly genericTypeArgs: PebbleAstType[] | undefined, readonly args: PebbleExpr[], readonly range: SourceRange ) {} diff --git a/src/ast/nodes/statements/TypeImplementsStmt.ts b/src/ast/nodes/statements/TypeImplementsStmt.ts index dd6eeac6..b6491718 100644 --- a/src/ast/nodes/statements/TypeImplementsStmt.ts +++ b/src/ast/nodes/statements/TypeImplementsStmt.ts @@ -3,7 +3,7 @@ import { Identifier } from "../common/Identifier"; import { HasSourceRange } from "../HasSourceRange"; import { NamedType } from "../types/NamedType"; import { FuncType } from "../types/NativeType"; -import { PebbleType } from "../types/PebbleType"; +import { PebbleAstType } from "../types/PebbleAstType"; import { BlockStmt } from "./BlockStmt"; @@ -11,8 +11,8 @@ export class TypeImplementsStmt implements HasSourceRange { constructor( - readonly typeIdentifier: PebbleType, - readonly interfaceType: PebbleType | undefined, + readonly typeIdentifier: PebbleAstType, + readonly interfaceType: PebbleAstType | undefined, readonly methodImplementations: InterfaceMethodImpl[], readonly range: SourceRange, ) {} @@ -23,7 +23,7 @@ export class InterfaceMethodImpl { constructor( readonly methodName: Identifier, - readonly typeParameters: PebbleType[], + readonly typeParameters: PebbleAstType[], readonly signature: FuncType, readonly body: BlockStmt, readonly range: SourceRange, diff --git a/src/ast/nodes/statements/declarations/FuncDecl.ts b/src/ast/nodes/statements/declarations/FuncDecl.ts index 74346900..cb8a39e1 100644 --- a/src/ast/nodes/statements/declarations/FuncDecl.ts +++ b/src/ast/nodes/statements/declarations/FuncDecl.ts @@ -7,7 +7,7 @@ import { HasSourceRange } from "../../HasSourceRange"; import { BlockStmt } from "../BlockStmt"; import { NamedType } from "../../types/NamedType"; import { FuncType } from "../../types/NativeType"; -import { PebbleType } from "../../types/PebbleType"; +import { PebbleAstType } from "../../types/PebbleAstType"; export class FuncDecl implements HasSourceRange @@ -15,7 +15,7 @@ export class FuncDecl constructor( readonly name: Identifier, readonly flags: CommonFlags, - readonly typeParams: PebbleType[], + readonly typeParams: PebbleAstType[], readonly signature: FuncType, readonly body: BlockStmt | PebbleExpr, readonly arrowKind: ArrowKind, diff --git a/src/ast/nodes/statements/declarations/InterfaceDecl.ts b/src/ast/nodes/statements/declarations/InterfaceDecl.ts index f6b46834..f1ba792e 100644 --- a/src/ast/nodes/statements/declarations/InterfaceDecl.ts +++ b/src/ast/nodes/statements/declarations/InterfaceDecl.ts @@ -4,14 +4,14 @@ import { HasSourceRange } from "../../HasSourceRange"; import { BlockStmt } from "../BlockStmt"; import { NamedType } from "../../types/NamedType"; import { FuncType } from "../../types/NativeType"; -import { PebbleType } from "../../types/PebbleType"; +import { PebbleAstType } from "../../types/PebbleAstType"; export class InterfaceDecl implements HasSourceRange { constructor( readonly name: Identifier, - readonly typeParams: PebbleType[], + readonly typeParams: PebbleAstType[], readonly methods: InterfaceDeclMethod[], readonly range: SourceRange ) {} @@ -22,7 +22,7 @@ export class InterfaceDeclMethod { constructor( readonly name: Identifier, - readonly typeParams: PebbleType[], + readonly typeParams: PebbleAstType[], readonly signature: FuncType, readonly body: BlockStmt | undefined, readonly range: SourceRange diff --git a/src/ast/nodes/statements/declarations/StructDecl.ts b/src/ast/nodes/statements/declarations/StructDecl.ts index f4896c3d..92a7fe55 100644 --- a/src/ast/nodes/statements/declarations/StructDecl.ts +++ b/src/ast/nodes/statements/declarations/StructDecl.ts @@ -1,7 +1,7 @@ import { SourceRange } from "../../../Source/SourceRange"; import { Identifier } from "../../common/Identifier"; import { HasSourceRange } from "../../HasSourceRange"; -import { PebbleType } from "../../types/PebbleType"; +import { PebbleAstType } from "../../types/PebbleAstType"; import { VarDecl } from "./VarDecl/VarDecl"; @@ -10,7 +10,7 @@ export class StructDecl { constructor( readonly name: Identifier, - readonly typeParams: PebbleType[], + readonly typeParams: PebbleAstType[], readonly constrs: StructConstrDecl[], readonly range: SourceRange ) {} diff --git a/src/ast/nodes/statements/declarations/TypeAliasDecl.ts b/src/ast/nodes/statements/declarations/TypeAliasDecl.ts index ffb04147..d1124123 100644 --- a/src/ast/nodes/statements/declarations/TypeAliasDecl.ts +++ b/src/ast/nodes/statements/declarations/TypeAliasDecl.ts @@ -1,14 +1,14 @@ import { SourceRange } from "../../../Source/SourceRange"; import { HasSourceRange } from "../../HasSourceRange"; -import { PebbleType } from "../../types/PebbleType"; +import { PebbleAstType } from "../../types/PebbleAstType"; export class TypeAliasDecl implements HasSourceRange { constructor( - readonly typeIdentifier: PebbleType, - readonly aliasedType: PebbleType, + readonly typeIdentifier: PebbleAstType, + readonly aliasedType: PebbleAstType, readonly range: SourceRange, ) {} } \ No newline at end of file diff --git a/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts b/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts index 442d5366..2dec54fa 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr.ts @@ -2,7 +2,7 @@ import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; import { HasSourceRange } from "../../../HasSourceRange"; -import { PebbleType } from "../../../types/PebbleType"; +import { PebbleAstType } from "../../../types/PebbleAstType"; import { HasInitExpr } from "./HasInit"; import { VarDecl } from "./VarDecl"; @@ -12,7 +12,7 @@ export class ArrayLikeDeconstr constructor( readonly elements: VarDecl[], readonly rest: Identifier | undefined, - readonly type: PebbleType | undefined, // just for the type checker, ususally this is inferred + readonly type: PebbleAstType | undefined, // just for the type checker, ususally this is inferred readonly initExpr: PebbleExpr | undefined, readonly range: SourceRange, ) {} diff --git a/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts index d0048262..49696dfe 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.ts @@ -2,7 +2,7 @@ import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; import { HasSourceRange } from "../../../HasSourceRange"; -import { PebbleType } from "../../../types/PebbleType"; +import { PebbleAstType } from "../../../types/PebbleAstType"; import { ISingleDeconstructVarDecl, SingleDeconstructVarDecl } from "./SingleDeconstructVarDecl"; import { VarDecl } from "./VarDecl"; @@ -13,7 +13,7 @@ export class NamedDeconstructVarDecl readonly name: Identifier, readonly fields: Map, readonly rest: Identifier | undefined, - readonly type: PebbleType | undefined, // can be undefined when use ad function parameter + readonly type: PebbleAstType | undefined, // can be undefined when use ad function parameter readonly initExpr: PebbleExpr | undefined, // can be undefined when use ad function parameter readonly range: SourceRange, ) {} diff --git a/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts index 2bfbea24..f27829ce 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.ts @@ -2,7 +2,7 @@ import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; import { HasSourceRange } from "../../../HasSourceRange"; -import { PebbleType } from "../../../types/PebbleType"; +import { PebbleAstType } from "../../../types/PebbleAstType"; import { HasInitExpr } from "./HasInit"; export class SimpleVarDecl @@ -10,7 +10,7 @@ export class SimpleVarDecl { constructor( readonly name: Identifier, - readonly type: PebbleType | undefined, + readonly type: PebbleAstType | undefined, readonly initExpr: PebbleExpr | undefined, readonly range: SourceRange, ) {} diff --git a/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts b/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts index 61f0d0b1..06370990 100644 --- a/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts +++ b/src/ast/nodes/statements/declarations/VarDecl/SingleDeconstructVarDecl.ts @@ -2,14 +2,14 @@ import { SourceRange } from "../../../../Source/SourceRange"; import { Identifier } from "../../../common/Identifier"; import { PebbleExpr } from "../../../expr/PebbleExpr"; import { HasSourceRange } from "../../../HasSourceRange"; -import { PebbleType } from "../../../types/PebbleType"; +import { PebbleAstType } from "../../../types/PebbleAstType"; import { HasInitExpr } from "./HasInit"; import { VarDecl } from "./VarDecl"; export interface ISingleDeconstructVarDecl extends HasInitExpr { fields: Map; rest: Identifier | undefined; - type: PebbleType | undefined; // could turn useful in generic types (even with one constr) + type: PebbleAstType | undefined; // could turn useful in generic types (even with one constr) initExpr: PebbleExpr | undefined; // can be undefined when use ad function parameter } @@ -19,7 +19,7 @@ export class SingleDeconstructVarDecl constructor( readonly fields: Map, readonly rest: Identifier | undefined, - readonly type: PebbleType | undefined, + readonly type: PebbleAstType | undefined, readonly initExpr: PebbleExpr | undefined, readonly range: SourceRange ) {} diff --git a/src/ast/nodes/types/NamedType.ts b/src/ast/nodes/types/NamedType.ts index 305f6b14..b712219f 100644 --- a/src/ast/nodes/types/NamedType.ts +++ b/src/ast/nodes/types/NamedType.ts @@ -1,13 +1,13 @@ import { SourceRange } from "../../Source/SourceRange"; import { Identifier } from "../common/Identifier"; import { HasSourceRange } from "../HasSourceRange"; -import { PebbleType } from "./PebbleType"; +import { PebbleAstType } from "./PebbleAstType"; export class NamedType implements HasSourceRange { constructor( readonly name: Identifier, - readonly tyArgs: PebbleType[], + readonly tyArgs: PebbleAstType[], readonly range: SourceRange ) {} } \ No newline at end of file diff --git a/src/ast/nodes/types/NativeType.ts b/src/ast/nodes/types/NativeType.ts index cae374d0..132a7ce3 100644 --- a/src/ast/nodes/types/NativeType.ts +++ b/src/ast/nodes/types/NativeType.ts @@ -1,16 +1,16 @@ import { SourceRange } from "../../Source/SourceRange"; import { HasSourceRange } from "../HasSourceRange"; import { VarDecl } from "../statements/declarations/VarDecl/VarDecl"; -import { PebbleType } from "./PebbleType"; +import { PebbleAstType } from "./PebbleAstType"; export type NativeType = VoidType | BooleanType | NumberType | BytesType - | NativeOptionalType - | ListType - | LinearMapType + | NativeOptionalType + | ListType + | LinearMapType | FuncType ; @@ -56,7 +56,7 @@ export class BytesType implements HasSourceRange ) {} } -export class NativeOptionalType implements HasSourceRange +export class NativeOptionalType implements HasSourceRange { constructor( readonly typeArg: TArg, @@ -64,7 +64,7 @@ export class NativeOptionalType implements HasSourceRan ) {} } -export class ListType implements HasSourceRange +export class ListType implements HasSourceRange { constructor( readonly typeArg: TArg, @@ -72,7 +72,7 @@ export class ListType implements HasSourceRange ) {} } -export class LinearMapType implements HasSourceRange +export class LinearMapType implements HasSourceRange { constructor( readonly keyTypeArg: KT, @@ -85,7 +85,7 @@ export class FuncType implements HasSourceRange { constructor( readonly params: VarDecl[], - readonly returnType: PebbleType | undefined, + readonly returnType: PebbleAstType | undefined, readonly range: SourceRange ) {} } \ No newline at end of file diff --git a/src/ast/nodes/types/PebbleType.ts b/src/ast/nodes/types/PebbleAstType.ts similarity index 76% rename from src/ast/nodes/types/PebbleType.ts rename to src/ast/nodes/types/PebbleAstType.ts index 30e9dcc0..a66fec8d 100644 --- a/src/ast/nodes/types/PebbleType.ts +++ b/src/ast/nodes/types/PebbleAstType.ts @@ -2,12 +2,12 @@ import { isObject } from "@harmoniclabs/obj-utils"; import { isNativeType, NativeType } from "./NativeType"; import { NamedType } from "./NamedType"; -export type PebbleType +export type PebbleAstType = NativeType | NamedType ; -export function isPebbleType( obj: any ): obj is PebbleType +export function isPebbleAstType( obj: any ): obj is PebbleAstType { return isObject( obj ) && ( isNativeType( obj ) || diff --git a/src/compiler/AstCompiler/AstCompiler.ts b/src/compiler/AstCompiler/AstCompiler.ts index 0d9b688f..8014da15 100644 --- a/src/compiler/AstCompiler/AstCompiler.ts +++ b/src/compiler/AstCompiler/AstCompiler.ts @@ -8,6 +8,15 @@ import { IPebbleCompiler } from "../IPebbleCompiler"; * compiles Pebble AST to Functional IR. * * AST -> FIR + * + * The AST is simply the result of tokenization and parsing. + * + * Therefore the AST is only syntactically correct, but not necessarily semantically correct. + * + * During the compilation from AST to FIR, + * missign types are inferred and the resulting FIR is checked for semantic correctness. + * + * In short, here is where type checking happens. */ export class AstCompiler extends DiagnosticEmitter implements IPebbleCompiler @@ -20,4 +29,13 @@ export class AstCompiler extends DiagnosticEmitter { super( diagnostics ); } + + async compileFile( + path: string, + src: string | undefined = undefined, + isEntry: boolean = true + ) + { + // src = src ?? this.io.readFile( path ) + } } \ No newline at end of file diff --git a/src/compiler/Scope.ts b/src/compiler/Scope.ts new file mode 100644 index 00000000..83648b98 --- /dev/null +++ b/src/compiler/Scope.ts @@ -0,0 +1,39 @@ + +export interface SymbolInfos { + /** `true` if the symbol indicates a type */ + isTypeSymbol: boolean; + /** + * if `isTypeSymbol` is `true`, + * this is the definition of the type. + * + * if `isTypeSymbol` is `false`, + * this is the type of the symbol. + */ + type: any; +} + +export class Scope +{ + private readonly symbols = new Map(); + constructor( + readonly parent: Scope | undefined, + // readonly name: string | undefined = undefined, + ) {} + + define( name: string, infos: SymbolInfos ): void + { + this.symbols.set( name, infos ); + } + + // to check for re-declarations + isDefinedInThisScope( name: string ): boolean + { + return this.symbols.has( name ); // || this.parent?.isDefined( name ); + } + + resolve( name: string ): SymbolInfos | undefined + { + return this.symbols.get( name ) ?? this.parent?.resolve( name ); + } + +} \ 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 new file mode 100644 index 00000000..5c8ba907 --- /dev/null +++ b/src/compiler/path/__tests__/compiler.path.test.ts @@ -0,0 +1,85 @@ +import { removeSingleDotDirsFromPath, dirname, resolveAsRootPath } from "../path"; + +describe("path", () => { + + describe("removeSingleDotDirsFromPath", () => { + + function testPath( inp: string, out: string ) + { + test(inp + " -> " + out, () => { + expect( removeSingleDotDirsFromPath( inp ) ).toEqual( out ); + }) + } + + testPath("./a/b/c" , "a/b/c"); + testPath("././a/b/c" , "a/b/c"); + testPath("a/b/c" , "a/b/c"); + testPath("./a/./b/./c" , "a/b/c"); + testPath("./a/b/././c" , "a/b/c"); + testPath("./a/b/c/." , "a/b/c"); + testPath("./././a/b/c" , "a/b/c"); + testPath("./a/././b/././c" , "a/b/c"); + + // final `./` tells us that `c` is a directory + testPath("./a/b/c/./" , "a/b/c/"); + testPath("./a/./b/./c/./" , "a/b/c/"); + + testPath("../../a/b/c", "../../a/b/c"); + testPath(".././../a/b/c", "../../a/b/c"); + testPath("../../a/./b/c", "../../a/b/c"); + + testPath("../../a/../b/c", "../../b/c"); + + }); + + describe("dirname", () => { + + function testPath( inp: string, out: string ) + { + test(inp + " -> " + out, () => { + expect( dirname( inp ) ).toEqual( out ); + }) + } + + testPath("./a/b/c" , "a/b/"); + testPath("././a/b/c" , "a/b/"); + testPath("a/b/c" , "a/b/"); + testPath("./a/./b/./c" , "a/b/"); + testPath("./a/b/././c" , "a/b/"); + testPath("./a/b/c/." , "a/b/"); + testPath("./././a/b/c" , "a/b/"); + testPath("./a/././b/././c" , "a/b/"); + + // final `./` tells us that `c` is a directory + testPath("./a/b/c/./" , "a/b/c/"); + testPath("./a/./b/./c/./" , "a/b/c/"); + + testPath("../../a/b/c", "../../a/b/"); + testPath("../../a/b/c/", "../../a/b/c/"); + testPath(".././../a/b/c", "../../a/b/"); + testPath("../../a/./b/c", "../../a/b/"); + + }); + + describe("resolveAsRootPath", () => { + + function testPath( inp: string, origin: string, out: string | undefined ) + { + test(inp + " from " + origin +" -> " + out, () => { + expect( resolveAsRootPath( inp, origin ) ).toEqual( out ); + }); + } + + testPath("../c","b","c"); + testPath("../c","a/b","a/c"); + testPath("../c/","a/b","a/c/"); + testPath("../../c/d/e","a/b/f","a/c/d/e"); + testPath("../../c/d/e/","a/b/f","a/c/d/e/"); + testPath("..","a/b/f","a/b/"); + + testPath("../../../","a/b/c","./"); + testPath("../../..","a/b/c","./"); + + testPath("../../../../","a/b/c", undefined ); + }) +}) \ No newline at end of file diff --git a/src/utils/mangleInternalPath.ts b/src/compiler/path/mangleInternalPath.ts similarity index 100% rename from src/utils/mangleInternalPath.ts rename to src/compiler/path/mangleInternalPath.ts diff --git a/src/compiler/path/path.ts b/src/compiler/path/path.ts new file mode 100644 index 00000000..2ce9daaf --- /dev/null +++ b/src/compiler/path/path.ts @@ -0,0 +1,169 @@ +/** + * @fileoverview Various file path utility. + * @license Apache-2.0 + */ + +import { + CharCode +} from "../../utils/text"; + +import { + PATH_DELIMITER +} from "../../common"; + +const separator = CharCode.Slash; + +/** + * Normalizes the specified path, removing interior placeholders. + * Expects a posix-compatible relative path (not Windows compatible). + */ +export function removeSingleDotDirsFromPath(path: string): string { + let pos = 0; + let len = path.length; + + // trim leading './' + while (pos + 1 < len && + path.charCodeAt(pos) === CharCode.Dot && + path.charCodeAt(pos + 1) === separator + ) { + pos += 2; + } + + if( pos > 0 || len < path.length ) { + path = path.substring(pos, len); + len -= pos; + pos = 0; + } + + let atEnd: boolean; + while (pos + 1 < len) { + atEnd = false; + + // we are only interested in '/.' sequences ... + if(!( + path.charCodeAt(pos) === separator && + path.charCodeAt(pos + 1) === CharCode.Dot + )) { + ++pos; + continue; + } + + // '/.' ( '/' | $ ) + atEnd = pos + 2 === len; + if (atEnd || + pos + 2 < len && + path.charCodeAt(pos + 2) === separator + ) { + path = atEnd + ? path.substring(0, pos) + : path.substring(0, pos) + path.substring(pos + 2); + len -= 2; + continue; + } + + // '/.' ( './' | '.' $ ) + atEnd = pos + 3 === len; + if (!( + atEnd && path.charCodeAt(pos + 2) === CharCode.Dot || + pos + 3 < len && + path.charCodeAt(pos + 2) === CharCode.Dot && + path.charCodeAt(pos + 3) === separator + )) + { + ++pos; + continue; + } + // find preceeding '/' + let ipos = pos; + while (--ipos >= 0) { + if( path.charCodeAt(ipos) !== separator ) continue; + + if (pos - ipos !== 3 || + path.charCodeAt(ipos + 1) !== CharCode.Dot || + path.charCodeAt(ipos + 2) !== CharCode.Dot + ) { // exclude '..' itself + path = atEnd + ? path.substring(0, ipos) + : path.substring(0, ipos) + path.substring(pos + 3); + len -= pos + 3 - ipos; + pos = ipos - 1; // incremented again at end of loop + } + break; + } + + // if there's no preceeding '/', trim start if non-empty + if (ipos < 0 && pos > 0) { + if (pos !== 2 || + path.charCodeAt(0) !== CharCode.Dot || + path.charCodeAt(1) !== CharCode.Dot + ) { // exclude '..' itself + path = path.substring(pos + 4); + len = path.length; + continue; + } + } + pos++; + } + // path = path.endsWith( PATH_DELIMITER ) ? path.substring(0, len - 1) : path; + return len > 0 ? path : "."; +} + +/** Resolves the specified path relative to the specified origin. */ +export function resolveAsRootPath( toResolve: string, fromPath: string ): string | undefined +{ + const fromDirname = dirname( fromPath ) + toResolve = removeSingleDotDirsFromPath( toResolve ); + + const fromDirs = fromDirname.split( PATH_DELIMITER ); + const toDirs = toResolve.split( PATH_DELIMITER ); + + for( let i = 0; i < toDirs.length; i++ ) + { + const dir = toDirs[i]; + if( dir === ".." ) + { + fromDirs.pop(); + toDirs.splice( i--, 1 ); + if( fromDirs.length === 0 ) + { + if( toDirs.length === 0 ) continue; + if( toDirs[0] === ".." ) return undefined; // out of project root + } + } + else if( dir !== "." ) + { + fromDirs.push( dir ); + } + } + + const result = removeSingleDotDirsFromPath( + fromDirs.join( PATH_DELIMITER ) + ); + return result === "." || result.endsWith( fromDirname.slice( 0, fromDirname.length - 1 ) ) ? + result + PATH_DELIMITER : + result; +} + +/** Obtains the directory portion of a normalized path. */ +export function dirname( path: string ): string { + const wasEndingWithSlash = path.endsWith( PATH_DELIMITER ); + path = removeSingleDotDirsFromPath( path ); + if( !path.endsWith( PATH_DELIMITER ) && wasEndingWithSlash ) path += PATH_DELIMITER; + + let pos = path.length; + if (pos <= 1) { + if (pos === 0) return "."; + if (path.charCodeAt(0) === separator) { + path = path.endsWith( PATH_DELIMITER ) ? path : path + PATH_DELIMITER; + return path; + } + } + + while (--pos > 0) { + if (path.charCodeAt(pos) === separator) { + path = path.substring(0, pos); + return path.endsWith( PATH_DELIMITER ) ? path : path + PATH_DELIMITER; + } + } + return "./"; +} diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index e26944e0..238d5b97 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -11,14 +11,14 @@ import { DiagnosticMessage } from "../diagnostics/DiagnosticMessage"; import { DiagnosticCode } from "../diagnostics/diagnosticMessages.generated"; import { Token } from "../tokenizer/Token"; import { Tokenizer } from "../tokenizer/Tokenizer"; -import { mangleInternalPath } from "../utils/mangleInternalPath"; -import { normalizePath } from "../utils/path"; +import { mangleInternalPath } from "../compiler/path/mangleInternalPath"; +import { normalizePath } from "../compiler/path/path"; import { isNonEmpty } from "../utils/isNonEmpty"; import { PebbleExpr } from "../ast/nodes/expr/PebbleExpr"; import { SourceRange } from "../ast/Source/SourceRange"; import { SimpleVarDecl } from "../ast/nodes/statements/declarations/VarDecl/SimpleVarDecl"; import { ArrayLikeDeconstr } from "../ast/nodes/statements/declarations/VarDecl/ArrayLikeDeconstr"; -import { PebbleType } from "../ast/nodes/types/PebbleType"; +import { PebbleAstType } from "../ast/nodes/types/PebbleAstType"; import { IdentifierHandling } from "../tokenizer/IdentifierHandling"; import { determinePrecedence, Precedence } from "./Precedence"; import { makeUnaryPrefixExpr } from "../ast/nodes/expr/unary/UnaryPrefixExpr"; @@ -267,12 +267,12 @@ export class Parser extends DiagnosticEmitter return statement; } - parseTypeParameters(): PebbleType[] | undefined + parseTypeParameters(): PebbleAstType[] | undefined { const tn = this.tn; // at '<': TypeParameter (',' TypeParameter)* '>' - const typeParams = new Array(); + const typeParams = new Array(); while( !tn.skip( Token.GreaterThan ) ) { const typeParam = this.parseType(); @@ -412,7 +412,7 @@ export class Parser extends DiagnosticEmitter tn.range(), "implements" ); - let interfaceType: PebbleType | undefined = undefined; + let interfaceType: PebbleAstType | undefined = undefined; if( !tn.skip( Token.OpenBrace ) ) { @@ -623,7 +623,7 @@ export class Parser extends DiagnosticEmitter const name = new Identifier( tn.readIdentifier(), tn.range() ); - let typeParams: PebbleType[] | undefined = undefined; + let typeParams: PebbleAstType[] | undefined = undefined; if( tn.skip( Token.LessThan ) ) { typeParams = this.parseTypeParameters(); @@ -689,7 +689,7 @@ export class Parser extends DiagnosticEmitter const name = new Identifier( tn.readIdentifier(), tn.range() ); - let typeParams: PebbleType[] | undefined = undefined; + let typeParams: PebbleAstType[] | undefined = undefined; if( tn.skip( Token.LessThan ) ) { typeParams = this.parseTypeParameters(); @@ -864,7 +864,7 @@ export class Parser extends DiagnosticEmitter private parseNamedFuncSig( flags: CommonFlags = CommonFlags.None, startPos?: number - ): [ Identifier, PebbleType[] | undefined, FuncType ] | undefined + ): [ Identifier, PebbleAstType[] | undefined, FuncType ] | undefined { const tn = this.tn; @@ -879,7 +879,7 @@ export class Parser extends DiagnosticEmitter const name = new Identifier( tn.readIdentifier(), tn.range() ); let sigStart = -1; - let typeParams: PebbleType[] | undefined = undefined; + let typeParams: PebbleAstType[] | undefined = undefined; if( tn.skip( Token.LessThan ) ) { sigStart = tn.tokenPos; @@ -899,7 +899,7 @@ export class Parser extends DiagnosticEmitter const params = this.parseParameters(); if( !params ) return undefined; - let returnType: PebbleType | undefined = undefined; + let returnType: PebbleAstType | undefined = undefined; if( tn.skip( Token.Colon ) ) { returnType = this.parseType(); @@ -1177,7 +1177,7 @@ export class Parser extends DiagnosticEmitter let isRest = false; let startRange: SourceRange | undefined = undefined let flags = CommonFlags.None; - let explicitType: PebbleType | undefined = undefined; + let explicitType: PebbleAstType | undefined = undefined; let initializer: PebbleExpr | undefined = undefined; while( !tn.skip( Token.CloseBrace ) ) { @@ -1265,7 +1265,7 @@ export class Parser extends DiagnosticEmitter { const tn = this.tn; - // at '[': ( VarDecl ','? )* ']' ( ':' PebbleType )? ( '=' PebbleExpr )? + // at '[': ( VarDecl ','? )* ']' ( ':' PebbleAstType )? ( '=' PebbleExpr )? const startPos = tn.pos; @@ -1356,16 +1356,16 @@ export class Parser extends DiagnosticEmitter } /** - * parses `(: PebbleType)? (= PebbleExpr)?` for parameters and variable declarations + * parses `(: PebbleAstType)? (= PebbleExpr)?` for parameters and variable declarations */ private _parseTypeAndInitializer( startRange: SourceRange = this.tn.range(), isRest: boolean = false, - ): [ type: PebbleType | undefined, initializer: PebbleExpr | undefined ] + ): [ type: PebbleAstType | undefined, initializer: PebbleExpr | undefined ] { const tn = this.tn; - let type: PebbleType | undefined = undefined; + let type: PebbleAstType | undefined = undefined; if( tn.skip(Token.Colon) ) type = this.parseType(); @@ -1384,7 +1384,7 @@ export class Parser extends DiagnosticEmitter parseType( suppressErrors: boolean = false - ): PebbleType | undefined + ): PebbleAstType | undefined { const tn = this.tn; @@ -1491,7 +1491,7 @@ export class Parser extends DiagnosticEmitter const name = new Identifier( tn.readIdentifier(), tn.range() ); - const params = new Array(); + const params = new Array(); if( tn.skip( Token.LessThan ) ) { @@ -2309,7 +2309,7 @@ export class Parser extends DiagnosticEmitter potentiallyGeneric: boolean = false ): PebbleExpr { const tn = this.tn; - let typeArguments: PebbleType[] | undefined = undefined; + let typeArguments: PebbleAstType[] | undefined = undefined; while ( tn.skip(Token.OpenParen) || potentiallyGeneric && @@ -2328,7 +2328,7 @@ export class Parser extends DiagnosticEmitter return callerExpr; } - tryParseTypeArgumentsBeforeArguments(): PebbleType[] | undefined + tryParseTypeArgumentsBeforeArguments(): PebbleAstType[] | undefined { const tn = this.tn; // at '<': Type (',' Type)* '>' '(' @@ -2337,7 +2337,7 @@ export class Parser extends DiagnosticEmitter if (!tn.skip(Token.LessThan)) return undefined; const startPos = tn.tokenPos; - let typeArguments: PebbleType[] = []; + let typeArguments: PebbleAstType[] = []; do { // closing '>' if (tn.peek() === Token.GreaterThan) break; @@ -2431,7 +2431,7 @@ export class Parser extends DiagnosticEmitter if (startPos < 0) startPos = name.range.start; if (signatureStart < 0) signatureStart = startPos; - let returnType: PebbleType | undefined = undefined; + let returnType: PebbleAstType | undefined = undefined; // either `function ( ... )` or `( ... )` // BUT NOT `param =>` diff --git a/src/utils/path.ts b/src/utils/path.ts deleted file mode 100644 index 27c6918c..00000000 --- a/src/utils/path.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @fileoverview Various file path utility. - * @license Apache-2.0 - */ - -import { - CharCode -} from "./text"; - -import { - PATH_DELIMITER -} from "../common"; - -const separator = CharCode.Slash; - -/** - * Normalizes the specified path, removing interior placeholders. - * Expects a posix-compatible relative path (not Windows compatible). - */ -export function normalizePath(path: string): string { - let pos = 0; - let len = path.length; - - // trim leading './' - while (pos + 1 < len && - path.charCodeAt(pos) === CharCode.Dot && - path.charCodeAt(pos + 1) === separator - ) { - pos += 2; - } - - if (pos > 0 || len < path.length) { - path = path.substring(pos, len); - len -= pos; - pos = 0; - } - - let atEnd: boolean; - while (pos + 1 < len) { - atEnd = false; - - // we are only interested in '/.' sequences ... - if ( - path.charCodeAt(pos) === separator && - path.charCodeAt(pos + 1) === CharCode.Dot - ) { - // '/.' ( '/' | $ ) - atEnd = pos + 2 === len; - if (atEnd || - pos + 2 < len && - path.charCodeAt(pos + 2) === separator - ) { - path = atEnd - ? path.substring(0, pos) - : path.substring(0, pos) + path.substring(pos + 2); - len -= 2; - continue; - } - - // '/.' ( './' | '.' $ ) - atEnd = pos + 3 === len; - if (atEnd && path.charCodeAt(pos + 2) === CharCode.Dot || - pos + 3 < len && - path.charCodeAt(pos + 2) === CharCode.Dot && - path.charCodeAt(pos + 3) === separator - ) { - // find preceeding '/' - let ipos = pos; - while (--ipos >= 0) { - if (path.charCodeAt(ipos) === separator) { - if (pos - ipos !== 3 || - path.charCodeAt(ipos + 1) !== CharCode.Dot || - path.charCodeAt(ipos + 2) !== CharCode.Dot - ) { // exclude '..' itself - path = atEnd - ? path.substring(0, ipos) - : path.substring(0, ipos) + path.substring(pos + 3); - len -= pos + 3 - ipos; - pos = ipos - 1; // incremented again at end of loop - } - break; - } - } - - // if there's no preceeding '/', trim start if non-empty - if (ipos < 0 && pos > 0) { - if (pos !== 2 || - path.charCodeAt(0) !== CharCode.Dot || - path.charCodeAt(1) !== CharCode.Dot - ) { // exclude '..' itself - path = path.substring(pos + 4); - len = path.length; - continue; - } - } - } - } - pos++; - } - return len > 0 ? path : "."; -} - -/** Resolves the specified path relative to the specified origin. */ -export function resolvePath(normalizedPath: string, origin: string): string { - if (normalizedPath.startsWith("std/")) { - return normalizedPath; - } - return normalizePath( - dirname(origin) + PATH_DELIMITER + normalizedPath - ); -} - -/** Obtains the directory portion of a normalized path. */ -export function dirname(normalizedPath: string): string { - let pos = normalizedPath.length; - if (pos <= 1) { - if (pos === 0) return "."; - if (normalizedPath.charCodeAt(0) === separator) { - return normalizedPath; - } - } - while (--pos > 0) { - if (normalizedPath.charCodeAt(pos) === separator) { - return normalizedPath.substring(0, pos); - } - } - return "."; -}