diff --git a/examples/LinkedList.plsm b/examples/LinkedList.plsm index b0940da..ee2d5de 100644 --- a/examples/LinkedList.plsm +++ b/examples/LinkedList.plsm @@ -1,22 +1,29 @@ -module test; +module std/list; -import stdlib/io; -import stdlib/heap; -import stdlib/err; +import std/io; +import std/heap; -trait Collection { - add(item: &T) -> void; - remove(item: &T) -> void; - contains(item: &T) -> bool; - size() -> u64; +trait Queue : Collection { + put(item: &T) void; + take() &T; + poll() &T?; + peek() &T?; +} - isEmpty() -> bool = self.size() > 0; - isNotEmpty() -> bool = self.size() <= 0; +pub trait Collection { + add(item: &T) void; + remove(item: &T) void; + contains(item: &T) bool; - each(fn: (item: &T, break: () -> void) -> void) -> void; + size() u64; - any(fn: (&T) -> bool) -> bool { + isEmpty() bool = self.size() > 0; + isNotEmpty() bool = self.size() <= 0; + + each(fn: (item: &T, break: () -> void) -> void) void; + + any(fn: (&T) -> bool) bool { let mut res = false; self.each((item, break) -> { @@ -26,10 +33,10 @@ trait Collection { } }); - return res; + ret res; } - all(fn: (&T) -> bool) -> bool { + all(fn: (&T) -> bool) bool { let mut res = true; self.each((item, break) -> { @@ -39,32 +46,25 @@ trait Collection { } }); - return res; + ret res; } // map(fn: (&T) -> &S) -> Collection; // filter(fn: (&T) -> bool) -> Collection; } -trait Queue : Collection { - put(item: &T) -> void; - take() -> &T; - poll() -> &T?; - peek() -> &T?; -} - trait Stack : Collection { - push(item: &T) -> void; - pop() -> &T; - peek() -> &T?; + push(item: &T) void; + pop() &T; + peek() &T?; } -struct LinkedList { +pub struct LinkedList { pub mut head: &T?; pub mut tail: &LinkedList?; mut size: u64; - init() -> void { + init() void { self.head = null; self.tail = null; self.size = 0; @@ -72,11 +72,11 @@ struct LinkedList { } impl Collection for LinkedList { - add(item: &T) -> void { + add(item: &T) void { if self.head == null { self.head = item; self.size = self.size + 1; - return; + ret; } if self.tail == null { @@ -88,9 +88,9 @@ impl Collection for LinkedList { self.size = self.size + 1; } - remove(item: &T) -> void { + remove(item: &T) void { if self.head == null { - return; + ret; } if self.head == item { @@ -104,36 +104,36 @@ impl Collection for LinkedList { } self.size = self.size - 1; - return; + ret; } self.tail.remove(item); self.size = self.size - 1; } - contains(item: &T) -> bool { + contains(item: &T) bool { if self.head == null { - return false; + ret false; } if self.head == item { - return true; + ret true; } - return self.tail.contains(item); + ret self.tail.contains(item); } - size() -> u64 { - return self.size; + size() u64 { + ret self.size; } - each(fn: (item: &T, break: () -> void) -> void) -> void { + each(fn: (item: &T, break: () -> void) -> void) void { if self.head == null { - return; + ret; } let mut continue = true; - fn(self.head, () -> { continue = false; }); + fn(self.head, () -> continue = false); if continue && self.tail != null { self.tail.each(fn); @@ -142,28 +142,28 @@ impl Collection for LinkedList { } impl Queue for LinkedList { - put(item: &T) -> void = self.add(item); + put(item: &T) void = self.add(item); - take() -> &T { + take() &T { if head == null { // TODO: wait for item to be available - return null; + ret null; } let item = self.head; self.remove(item); - return item; + ret item; } - poll() -> &T? { + poll() &T? { if self.head == null { - return null; + ret null; } let item = self.head; self.remove(item); - return item; + ret item; } - peek() -> &T? = self.head; + peek() &T? = self.head; } diff --git a/examples/heap.plsm b/examples/heap.plsm index 92c8e1d..f77fa2b 100644 --- a/examples/heap.plsm +++ b/examples/heap.plsm @@ -1,14 +1,21 @@ -module memory/heap; +module std/heap; +pub fun alloc(size: u64) i32 { + ret 10; +} + +/* fun alloc(size: u64) { // TODO } fun new(_struct: Struct) { + // TODO +} +*/ -} - -unsafe fun memset(addr: &u8, count: u64, value: u8) { +/* +fun memset(addr: &u8, count: u64, value: u8) { let i = 0; while (i < count) { addr[i] = value; @@ -18,8 +25,9 @@ unsafe fun memset(addr: &u8, count: u64, value: u8) { struct Array { ptr: &T; - + ([])(index: u64) { } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/examples/stdio.plsm b/examples/stdio.plsm new file mode 100644 index 0000000..b1642bf --- /dev/null +++ b/examples/stdio.plsm @@ -0,0 +1,9 @@ +module std/io; + +pub fun println() i8 { + ret 10; +} + +pub struct Buffer { + ptr: &i8; +} diff --git a/examples/test.plsm b/examples/test.plsm new file mode 100644 index 0000000..9e8a0e9 --- /dev/null +++ b/examples/test.plsm @@ -0,0 +1,10 @@ +module test; + +import std/list; + +let glob = 10; + +fun main() i32 { + let local = glob + 10; + ret local; +} diff --git a/src/antlr/plsm.g4 b/src/antlr/plsm.g4 index 0684811..e71c731 100644 --- a/src/antlr/plsm.g4 +++ b/src/antlr/plsm.g4 @@ -54,6 +54,7 @@ importStmt $ast = new AST.ImportStmt( this.getSourceRange($ctx), $qualifiers.map(q => q.text), + $alias.text ?? null, ); }; @@ -103,7 +104,7 @@ funDecl returns[AST.FunDecl ast = null as any]: pub = 'pub'? 'fun' name = ID ( '(' (params += funParam (',' params += funParam)* ','?)? ')' - )? '->' returnTypeName = typeName '->' ( + )? returnTypeName = typeName ( '=' exprBody = expr ';' | blockBody = block ) { @@ -125,10 +126,10 @@ funDecl funParam returns[AST.FunParam ast = null as any]: - name = ID ':' typeName { + (name = ID ':')? typeName { $ast = new AST.FunParam( this.getSourceRange($ctx), - $name.text!, + $ctx._name?.text ?? null, $typeName.ast, ); }; @@ -197,7 +198,7 @@ memberMethod returns[AST.MemberMethod ast = null as any]: pub = 'pub'? name = ID '(' ( params += funParam (',' params += funParam)* ','? - )? ')' '->' returnTypeName = typeName ( + )? ')' returnTypeName = typeName ( ';' | blockBody = block | '=' exprBody = expr ';' @@ -220,7 +221,7 @@ memberMethod returnStmt returns[AST.ReturnStmt ast = null as any]: - 'return' expr? ';' { + 'ret' expr? ';' { $ast = new AST.ReturnStmt( this.getSourceRange($ctx), $ctx.expr()?.ast ?? null, @@ -329,19 +330,21 @@ typeName namedTypeName returns[AST.NamedTypeName ast = null as any]: - name = ID { + name = qualifiedName { $ast = new AST.NamedTypeName( this.getSourceRange($ctx), - $name.text!, + $name.name, ); }; genericTypeName returns[AST.GenericTypeName ast = null as any]: - name = ID '<' typeArgs += typeName (',' typeArgs += typeName)* '>' { + name = qualifiedName '<' typeArgs += typeName ( + ',' typeArgs += typeName + )* '>' { $ast = new AST.GenericTypeName( this.getSourceRange($ctx), - $name.text!, + $name.name, $typeArgs.map(arg => arg.ast), ); }; diff --git a/src/frontend/analysis.ts b/src/frontend/analysis.ts new file mode 100644 index 0000000..d44a0a1 --- /dev/null +++ b/src/frontend/analysis.ts @@ -0,0 +1,238 @@ +import { raiseError } from "~/errors"; +import AST from "./ast"; +import { getModule, putModule } from "./modules"; +import { FunSymbol, StructSymbol, Symbol, TraitSymbol, VarSymbol } from "./semantics/symbol"; +import { FunType, GenericType, PrimitiveType, StructType, TraitType, Type } from "./semantics/type"; + +class ScopedTable { + private scopes: Record[] = []; + + public insert(name: string, entry: T) { + this.scopes[this.scopes.length - 1][name] = entry; + } + + public lookup(name: string) { + const found: T[] = []; + + for (let i = this.scopes.length - 1; i >= 0; i--) { + if (name in this.scopes[i]) { + found.push(this.scopes[i][name]); + } + } + + return found; + } + + public enterScope() { + this.scopes.push({}); + } + + public popScope() { + this.scopes.push({}); + } +} + +class ResolveImports extends AST.BaseVisitor { + public visited: AST.Module[] = []; + + public visitModule(module_: AST.Module) { + if (this.visited.includes(module_)) return; + this.visited.push(module_); + + module_.imports.forEach(i => i.accept(this)); + } + + public visitImportStmt(importStmt: AST.ImportStmt) { + if (importStmt.module) return; + + importStmt.module = getModule(importStmt.path.join('/')); + + if (!importStmt.module) { + raiseError(importStmt.sourceRange, `could not resolve import '${importStmt.path.join('/')}': module not found`); + return; + } + + importStmt.module.accept(this); + } +} + +class LoadModuleDefinitions extends AST.BaseVisitor { + private currentModule: AST.Module = null as any; + + public visitModule(module_: AST.Module) { + this.currentModule = module_; + super.visitModule(module_); + } + + public visitVarDecl(varDecl: AST.VarDecl) { + if (this.currentModule.hasSymbol(varDecl.name)) { + raiseError(varDecl.sourceRange, `redefinition of symbol '${varDecl.name}'`); + return; + } + + this.currentModule.addSymbol(new VarSymbol( + varDecl.name, + varDecl.isMut, + )); + } + + public visitFunDecl(funDecl: AST.FunDecl) { + if (this.currentModule.hasSymbol(funDecl.name)) { + raiseError(funDecl.sourceRange, `redefinition of symbol '${funDecl.name}'`); + return; + } + + this.currentModule.addSymbol(new FunSymbol( + funDecl.name, + funDecl.isPub, + new FunType(), + )); + } + + public visitStructDecl(structDecl: AST.StructDecl) { + if (this.currentModule.hasSymbol(structDecl.name)) { + raiseError(structDecl.sourceRange, `redefinition of symbol '${structDecl.name}'`); + return; + } + + this.currentModule.addSymbol(new StructSymbol( + structDecl.name, + structDecl.isPub, + new StructType(structDecl.name), + )); + } + + public visitTraitDecl(traitDecl: AST.TraitDecl) { + if (this.currentModule.hasSymbol(traitDecl.name)) { + raiseError(traitDecl.sourceRange, `redefinition of symbol '${traitDecl.name}'`); + return; + } + + const type_ = new TraitType(traitDecl.name); + + const genericNames: string[] = []; + traitDecl.genericParams.forEach((param) => { + if (genericNames.includes(param)) { + raiseError(traitDecl.sourceRange, `redefinition of generic type name '${param}'`); + } + + genericNames.push(param); + type_.generics.push([param, new GenericType()]); + }); + + const symbol = new TraitSymbol( + traitDecl.name, + traitDecl.isPub, + type_, + ); + + traitDecl.symbol = symbol; + + this.currentModule.addSymbol(symbol); + } + + public visitImplDecl(implDecl: AST.ImplDecl) { } +} + +class ExtendModuleDefinitions extends AST.BaseVisitor { + public types = new ScopedTable(); + + public traits = new Map(); + + public visitModule(module_: AST.Module) { + this.types = new ScopedTable(); + this.types.enterScope(); + + this.types.insert('void', PrimitiveType.Void); + this.types.insert('bool', PrimitiveType.Bool); + this.types.insert('i8', PrimitiveType.I8); + this.types.insert('i16', PrimitiveType.I16); + this.types.insert('i32', PrimitiveType.I32); + this.types.insert('i64', PrimitiveType.I64); + this.types.insert('u8', PrimitiveType.U8); + this.types.insert('u16', PrimitiveType.U16); + this.types.insert('u32', PrimitiveType.U32); + this.types.insert('u64', PrimitiveType.U64); + this.types.insert('f32', PrimitiveType.F32); + this.types.insert('f64', PrimitiveType.F64); + + const importedModuleNames: string[] = []; + module_.imports.forEach(import_ => { + if (!import_.module) return; + + const moduleTypes: Record = {}; + import_.module.getPublicSymbols().forEach(symbol => { + if (symbol instanceof FunSymbol) return; + moduleTypes[symbol.name] = symbol.type!; + }); + + const alias = import_.alias ?? import_.path[import_.path.length - 1]; + if (importedModuleNames.includes(alias)) { + raiseError(import_.sourceRange, `duplicate import of named module '${alias}', consider renaming the import`); + return; + } + + Object.entries(moduleTypes).forEach(([name, type]) => this.types.insert(`${alias}.${name}`, type)); + importedModuleNames.push(alias); + }); + + module_.getSymbols().forEach(symbol => { + if (!symbol.type) return; + this.types.insert(symbol.name, symbol.type); + }); + + super.visitModule(module_); + + this.types.popScope(); + } + + public visitTraitDecl(traitDecl: AST.TraitDecl) { + this.types.enterScope(); + + traitDecl.symbol?.type?.generics.forEach(([name, type]) => { + this.types.insert(name, type); + }); + + traitDecl.parentTraitTypeNames.forEach((typeName) => { + const name = typeName.path.join('.'); + const type = this.types.lookup(name)[0]; + + if (!type) { + raiseError(typeName.sourceRange, `could not resolve type name '${name}'`); + return; + } + + if (!(type instanceof TraitType)) { + raiseError(typeName.sourceRange, `cannot inherit from '${name}', it not a trait type`); + return; + } + + }); + + super.visitTraitDecl(traitDecl); + + this.types.popScope(); + } + + public visitMemberMethod(memberMethod: AST.MemberMethod) { + + } +} + + +export function performNameAndTypeAnalysis(module_: AST.Module) { + putModule(module_.moduleDecl.path.join('/'), module_); + + const resolveImports = new ResolveImports(); + module_.accept(resolveImports); + + const allModules = resolveImports.visited; + + allModules.forEach(module => { + module.accept(new LoadModuleDefinitions()); + }); + + allModules.forEach(module => { + module.accept(new ExtendModuleDefinitions()); + }); +} diff --git a/src/frontend/ast/decl.ts b/src/frontend/ast/decl.ts index 53fe354..00ca220 100644 --- a/src/frontend/ast/decl.ts +++ b/src/frontend/ast/decl.ts @@ -1,3 +1,4 @@ +import type { TraitSymbol } from "../semantics/symbol"; import type { Block } from "./block"; import type { Expr } from "./expr"; import { Node, SourceRange } from "./node"; @@ -39,7 +40,7 @@ export class FunDecl extends Decl { export class FunParam extends Node { constructor( sourceRange: SourceRange, - public name: string, + public name: string | null, public typeName: TypeName, ) { super(sourceRange); } @@ -49,12 +50,14 @@ export class FunParam extends Node { } export class TraitDecl extends Decl { + public symbol: TraitSymbol | null = null; + constructor( sourceRange: SourceRange, public isPub: boolean, public name: string, public genericParams: string[], - public parents: (NamedTypeName | GenericTypeName)[], + public parentTraitTypeNames: (NamedTypeName | GenericTypeName)[], public methods: MemberMethod[], ) { super(sourceRange); } diff --git a/src/frontend/ast/module.ts b/src/frontend/ast/module.ts index f1ad36d..fa506a6 100644 --- a/src/frontend/ast/module.ts +++ b/src/frontend/ast/module.ts @@ -1,9 +1,12 @@ +import type { Symbol } from "../semantics/symbol"; import type { Decl } from "./decl"; import { Node, SourceRange } from "./node"; import { Stmt } from "./stmt"; import type { Visitor } from "./visitor"; export class Module extends Node { + public symbols: Record = {}; + constructor( sourceRange: SourceRange, public moduleDecl: ModuleDecl, @@ -14,6 +17,24 @@ export class Module extends Node { accept(visitor: Visitor): ResultT { return visitor.visitModule(this); } + + addSymbol(symbol: Symbol) { + this.symbols[symbol.name] = symbol; + } + + hasSymbol(name: string): boolean { + return name in this.symbols; + } + + getSymbols() { + return Object.values(this.symbols); + } + + getPublicSymbols() { + return this.getSymbols().filter((sym) => { + return 'isPub' in sym && sym.isPub; + }); + } } export class ModuleDecl extends Stmt { @@ -27,7 +48,13 @@ export class ModuleDecl extends Stmt { } export class ImportStmt extends Stmt { - constructor(sourceRange: SourceRange, public path: string[]) { + public module: Module | null = null; + + constructor( + sourceRange: SourceRange, + public path: string[], + public alias: string | null = null, + ) { super(sourceRange); } diff --git a/src/frontend/ast/type-name.ts b/src/frontend/ast/type-name.ts index 5e83a59..9359a62 100644 --- a/src/frontend/ast/type-name.ts +++ b/src/frontend/ast/type-name.ts @@ -6,7 +6,7 @@ export abstract class TypeName extends Node { } export class NamedTypeName extends TypeName { constructor( sourceRange: SourceRange, - public name: string, + public path: string[], ) { super(sourceRange); } accept(visitor: Visitor): ResultT { @@ -17,7 +17,7 @@ export class NamedTypeName extends TypeName { export class GenericTypeName extends TypeName { constructor( sourceRange: SourceRange, - public name: string, + public path: string[], public args: TypeName[], ) { super(sourceRange); } diff --git a/src/frontend/ast/visitor.ts b/src/frontend/ast/visitor.ts index 4e5b97c..3234fc3 100644 --- a/src/frontend/ast/visitor.ts +++ b/src/frontend/ast/visitor.ts @@ -25,7 +25,6 @@ export abstract class Visitor { public abstract visitMemberField(memberField: MemberField): ResultT; public abstract visitMemberMethod(memberMethod: MemberMethod): ResultT; - public abstract visitIdentifier(identifier: Identifier): ResultT; public abstract visitIntExpr(intExpr: IntExpr): ResultT; public abstract visitFloatExpr(floatExpr: FloatExpr): ResultT; @@ -48,8 +47,12 @@ export abstract class Visitor { export class BaseVisitor implements Visitor { public visitModule(module_: Module): ResultT { + // console.log(module_.moduleDecl.path); module_.imports.forEach((import_) => import_.accept(this)); - module_.decls.forEach((decl) => decl.accept(this)); + module_.decls.forEach((decl) => { + // console.log(decl); + decl.accept(this); + }); return null as any; } @@ -108,11 +111,13 @@ export class BaseVisitor implements Visitor { } public visitTraitDecl(traitDecl: TraitDecl): ResultT { + traitDecl.parentTraitTypeNames.forEach((parent) => parent.accept(this)); traitDecl.methods.forEach((method) => method.accept(this)); return null as any; } public visitImplDecl(implDecl: ImplDecl): ResultT { + implDecl.traitTypeName.accept(this); implDecl.methods.forEach((attr) => attr.accept(this)); return null as any; } diff --git a/src/frontend/modules.ts b/src/frontend/modules.ts new file mode 100644 index 0000000..d488127 --- /dev/null +++ b/src/frontend/modules.ts @@ -0,0 +1,49 @@ +import { connect, Glob } from 'bun'; +import type AST from './ast'; +import { getFile } from '~/io/files'; +import fs from 'fs'; +import { parseModule } from '~/parser'; + +const modules: Record = {}; +let moduleFilePaths: Record | null = null; + +function getModuleFilePaths() { + if (!!moduleFilePaths) return moduleFilePaths; + moduleFilePaths = {}; + + const glob = new Glob('**/*.plsm'); + + const allFiles = [...glob.scanSync('.')]; + if (fs.existsSync('/usr/local/plsm')) { + allFiles.push(...glob.scanSync('/usr/local/plsm')); + } + + for (const file of allFiles) { + const { content, path } = getFile(file); + + const regex = /^module\s+([a-zA-Z_]+(\s*\/\s*[a-zA-Z_]+)*)\s*\;/; + let [, name] = [...(regex.exec(content) ?? ['', ''])]; + name = name.replaceAll(/\s+/g, ''); + + if (!name.trim().length) continue; + moduleFilePaths[name] = path; + } + + return moduleFilePaths; +} + +export function putModule(name: string, module: AST.Module) { + modules[name] = module; +} + +export function getModule(name: string) { + if (modules[name]) return modules[name]; + + const moduleFilePaths = getModuleFilePaths(); + if (!moduleFilePaths[name]) return null; + + const module = parseModule(moduleFilePaths[name]); + modules[name] = module; + + return module; +} diff --git a/src/frontend/semantics/symbol.ts b/src/frontend/semantics/symbol.ts index 9e64fd4..e8994c3 100644 --- a/src/frontend/semantics/symbol.ts +++ b/src/frontend/semantics/symbol.ts @@ -1,8 +1,76 @@ -import type { Type } from "./type"; +import type { FunType, StructType, TraitType, Type } from "./type"; -export class Symbol { +export abstract class Symbol { + abstract name: string; + abstract type: Type | null; + abstract isMut: boolean; +} + +export class VarSymbol extends Symbol { constructor( public name: string, - public type: Type, - ) { } -} \ No newline at end of file + public isMut: boolean, + public type: Type | null = null, + ) { + super(); + } +} + +export class FunSymbol extends Symbol { + public isMut = false; + + constructor( + public name: string, + public isPub: boolean, + public type: FunType | null = null, + ) { + super(); + } +} + +export class StructSymbol extends Symbol { + public isMut = false; + + constructor( + public name: string, + public isPub: boolean, + public type: StructType | null = null, + ) { + super(); + } +} +export class TraitSymbol extends Symbol { + public isMut = false; + + constructor( + public name: string, + public isPub: boolean, + public type: TraitType | null = null, + ) { + super(); + } +} + +// export abstract class TypeSymbol { +// abstract name: string; +// } + +// export class TraitSymbol extends TypeSymbol { +// constructor( +// public name: string, +// public isPub: boolean, +// public typeParams: string[], +// public parents: TraitType[] = [], +// public methods: [string, FunType][] = [], +// ) { super(); } +// } + +// export class StructSymbol extends TypeSymbol { +// constructor( +// public name: string, +// public isPub: boolean, +// public typeParams: string[], +// public fields: [string, Type | null][] = [], +// public methods: [string, FunType | null][] = [], +// ) { super(); } +// } diff --git a/src/frontend/semantics/type.ts b/src/frontend/semantics/type.ts index 3c83ded..3040641 100644 --- a/src/frontend/semantics/type.ts +++ b/src/frontend/semantics/type.ts @@ -1,17 +1,399 @@ -export abstract class Type { +import type AST from "../ast"; +import type { FunSymbol, StructSymbol, TraitSymbol, VarSymbol } from "./symbol"; +export abstract class Type { + abstract equals(other: Type): boolean; + abstract canBeCastedTo(other: Type): boolean; + abstract canBeAssignedTo(other: Type): boolean; + + canBeStored(): boolean { + return true; + } + + index(name: string): Type | null { + return null; + } + + replaceType(oldType: Type, newType: Type): Type { + if (this.equals(oldType)) { + return newType; + } + + return this; + } + + clone(): Type { + return this; + } } -export class OpaqueType extends Type { +export class GenericType extends Type { constructor() { super(); } + + equals(other: Type): boolean { + return this === other; + } + + canBeAssignedTo(other: Type): boolean { + return false; + } + + canBeCastedTo(other: Type): boolean { + return false; + } + + canBeStored(): boolean { + return false; + } +} + +function isOneOf(type: Type, types: Type[]): boolean { + return types.includes(type); +}; + +function isNumberType(type: Type): boolean { + return [ + PrimitiveType.I8, + PrimitiveType.I16, + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U8, + PrimitiveType.U16, + PrimitiveType.U32, + PrimitiveType.U64, + PrimitiveType.F32, + PrimitiveType.F64, + ].includes(type); +} + +export abstract class PrimitiveType extends Type { + static Void = new class extends PrimitiveType { + equals(other: Type): boolean { + return other === PrimitiveType.Void; + } + + canBeCastedTo(other: Type): boolean { + return other === PrimitiveType.Void; + } + + canBeAssignedTo(other: Type): boolean { + return false; + } + + canBeStored(): boolean { + return false; + } + }; + + static Bool = new class extends PrimitiveType { + equals(other: Type): boolean { + return other === PrimitiveType.Bool; + } + + canBeCastedTo(other: Type): boolean { + return this.equals(other) || isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return this.equals(other); + } + }; + + static I8 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.I8; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I8, + PrimitiveType.I16, + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U8, + PrimitiveType.U16, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static I16 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.I16; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I16, + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U16, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static I32 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.I32; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static I64 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.I64; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I64, + PrimitiveType.U64, + ]); + } + }; + + static U8 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.U8; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I16, + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U8, + PrimitiveType.U16, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static U16 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.U16; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I32, + PrimitiveType.I64, + PrimitiveType.U16, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static U32 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.U32; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.I64, + PrimitiveType.U32, + PrimitiveType.U64, + ]); + } + }; + + static U64 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.U64; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.U64, + ]); + } + }; + + static F32 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.F32; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.F32, + PrimitiveType.F64, + ]); + } + }; + + static F64 = new class extends PrimitiveType { + constructor() { super(); } + + equals(other: Type): boolean { + return other === PrimitiveType.F64; + } + + canBeCastedTo(other: Type): boolean { + return isNumberType(other); + } + + canBeAssignedTo(other: Type): boolean { + return isOneOf(other, [ + PrimitiveType.F64, + ]); + } + }; +} + +export class PointerType extends Type { + constructor(public type: Type) { super(); } + + equals(other: Type): boolean { + if (other instanceof PointerType) { + return this.type.equals(other.type); + } + + return false; + } + + canBeCastedTo(other: Type): boolean { + return this.canBeAssignedTo(other); + } + + canBeAssignedTo(other: Type): boolean { + return this.equals(other); + } +} + +export class FunType extends Type { + constructor( + public params: [string | null, Type][] = [], + public returnType: Type | null = null, + ) { super(); } + + equals(other: Type): boolean { + if (!(other instanceof FunType)) return false; + if (this.params.length !== other.params.length) return false; + if (!other.returnType || !this.returnType?.equals(other.returnType)) return false; + + for (let i = 0; i < this.params.length; i++) { + if (!this.params[i][1].equals(other.params[i][1])) return false; + } + + return true; + } + + canBeAssignedTo(other: Type): boolean { + return this.equals(other); + } + + canBeCastedTo(other: Type): boolean { + return this.equals(other); + } + + replaceType(oldType: Type, newType: Type): Type { + if (this.equals(oldType)) { + return newType; + } + + this.params = this.params.map(([name, type]) => [name, type.replaceType(oldType, newType)]); + this.returnType = this.returnType?.replaceType(oldType, newType) ?? null; + + return this; + } + + clone(): FunType { + return new FunType( + this.params.map(([name, type]) => [name, type.clone()]), + this.returnType?.clone() ?? null, + ); + } } export class StructType extends Type { constructor( public name: string, - public fields: [string, Type][], + public generics: [string, GenericType][] = [], + public fields: [string, Type][] = [], + public impls: TraitType[] = [], ) { super(); } + equals(other: Type): boolean { + return this === other; + } + + canBeCastedTo(other: Type): boolean { + return this.canBeAssignedTo(other); + } + + canBeAssignedTo(other: Type): boolean { + return this === other || this.impls.includes(other as any); + } + public hasField(field: string) { for (const [name] of this.fields) { if (name === field) return true; @@ -35,11 +417,80 @@ export class StructType extends Type { throw new Error(`StructType.getFieldOffset(...): field '${field}' not found'`); } + + replaceType(oldType: Type, newType: Type): Type { + if (this.equals(oldType)) { + return newType; + } + + this.fields = this.fields.map(([name, type]) => [name, type.replaceType(oldType, newType)]); + this.impls = this.impls.map(impl => impl.replaceType(oldType, newType) as TraitType); + this.generics = this.generics.map(([name, type]) => [name, type.replaceType(oldType, newType)] as any).filter(([, type]) => type instanceof GenericType); + + return this; + } + + isGeneric(): boolean { + return false; + } + + clone(): StructType { + return new StructType( + this.name, + this.generics.map(([name, type]) => [name, type.clone()]), + this.fields.map(([name, type]) => [name, type.clone()]), + this.impls.map((impl) => impl.clone()), + ); + } } export class TraitType extends Type { constructor( public name: string, - public args: Type[], + public parents: TraitType[] = [], + public methods: [string, FunType][] = [], + public generics: [string, GenericType][] = [], ) { super(); } + + equals(other: Type): boolean { + return this === other; + } + + canBeCastedTo(other: Type): boolean { + return this.canBeAssignedTo(other); + } + + canBeAssignedTo(other: Type): boolean { + return this.parents.includes(other as TraitType); + } + + isGeneric(): boolean { + return this.generics.length > 0; + } + + instantiateGeneric(genericArgs: Type[]) { + return + } + + replaceType(oldType: Type, newType: Type): Type { + if (this.equals(oldType)) { + return newType; + } + + this.parents = this.parents.map(parent => parent.replaceType(oldType, newType) as TraitType); + this.methods = this.methods.map(([name, type]) => [name, type.replaceType(oldType, newType) as FunType]); + this.generics = this.generics.map(([name, type]) => [name, type.replaceType(oldType, newType)] as any).filter(([, type]) => type instanceof GenericType); + + return this; + + } + + clone(): TraitType { + return new TraitType( + this.name, + this.parents.map(parent => parent.clone()), + this.methods.map(([name, type]) => [name, type.clone()]), + this.generics.map(([name, type]) => [name, type.clone()]), + ); + } } diff --git a/src/main.ts b/src/main.ts index 6c3a3ce..17aad69 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,9 +2,13 @@ import { inspect } from "bun"; import { parseModule } from "./parser"; import { hasErrors, printErrors } from "./errors"; import AST from "./frontend/ast"; +import { performNameAndTypeAnalysis } from "./frontend/analysis"; -const module = parseModule('examples/LinkedList.plsm'); -console.log(inspect(module, { depth: 1000, colors: false })); +const module = parseModule('examples/test.plsm'); +// console.log(inspect(module, { depth: 1000, colors: false })); -printErrors(); -module.accept(new AST.BaseVisitor()); \ No newline at end of file +// printErrors(); +// module.accept(new AST.BaseVisitor()); + +performNameAndTypeAnalysis(module); +printErrors(); \ No newline at end of file