general update

This commit is contained in:
Ludwig Lehnert 2025-03-18 19:49:59 +01:00
parent 63ab8998d4
commit 0e37260a72
14 changed files with 960 additions and 85 deletions

View File

@ -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<T> {
add(item: &T) -> void;
remove(item: &T) -> void;
contains(item: &T) -> bool;
size() -> u64;
trait Queue<T> : Collection<T> {
put(item: &T) void;
take() &T;
poll() &T?;
peek() &T?;
}
isEmpty() -> bool = self.size() > 0;
isNotEmpty() -> bool = self.size() <= 0;
pub trait Collection<T> {
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<T> {
}
});
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<T> {
}
});
return res;
ret res;
}
// map<S>(fn: (&T) -> &S) -> Collection<S>;
// filter(fn: (&T) -> bool) -> Collection<T>;
}
trait Queue<T> : Collection<T> {
put(item: &T) -> void;
take() -> &T;
poll() -> &T?;
peek() -> &T?;
}
trait Stack<T> : Collection<T> {
push(item: &T) -> void;
pop() -> &T;
peek() -> &T?;
push(item: &T) void;
pop() &T;
peek() &T?;
}
struct LinkedList<T> {
pub struct LinkedList<T> {
pub mut head: &T?;
pub mut tail: &LinkedList<T>?;
mut size: u64;
init() -> void {
init() void {
self.head = null;
self.tail = null;
self.size = 0;
@ -72,11 +72,11 @@ struct LinkedList<T> {
}
impl Collection<T> for LinkedList<T> {
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<T> for LinkedList<T> {
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<T> for LinkedList<T> {
}
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<T> for LinkedList<T> {
}
impl Queue<T> for LinkedList<T> {
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;
}

View File

@ -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<T> {
ptr: &T;
([])(index: u64) {
}
}
}
*/

9
examples/stdio.plsm Normal file
View File

@ -0,0 +1,9 @@
module std/io;
pub fun println() i8 {
ret 10;
}
pub struct Buffer {
ptr: &i8;
}

10
examples/test.plsm Normal file
View File

@ -0,0 +1,10 @@
module test;
import std/list;
let glob = 10;
fun main() i32 {
let local = glob + 10;
ret local;
}

View File

@ -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),
);
};

238
src/frontend/analysis.ts Normal file
View File

@ -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<T> {
private scopes: Record<string, T>[] = [];
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<Type>();
public traits = new Map<TraitType, TraitSymbol>();
public visitModule(module_: AST.Module) {
this.types = new ScopedTable<Type>();
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<string, Type> = {};
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());
});
}

View File

@ -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); }

View File

@ -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<string, Symbol> = {};
constructor(
sourceRange: SourceRange,
public moduleDecl: ModuleDecl,
@ -14,6 +17,24 @@ export class Module extends Node {
accept<ResultT>(visitor: Visitor<ResultT>): 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);
}

View File

@ -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<ResultT>(visitor: Visitor<ResultT>): 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); }

View File

@ -25,7 +25,6 @@ export abstract class Visitor<ResultT = any> {
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<ResultT = any> {
export class BaseVisitor<ResultT = any> implements Visitor<ResultT> {
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<ResultT = any> implements Visitor<ResultT> {
}
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;
}

49
src/frontend/modules.ts Normal file
View File

@ -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<string, AST.Module> = {};
let moduleFilePaths: Record<string, string> | 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;
}

View File

@ -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,
) { }
}
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(); }
// }

View File

@ -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()]),
);
}
}

View File

@ -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());
// printErrors();
// module.accept(new AST.BaseVisitor());
performNameAndTypeAnalysis(module);
printErrors();