general update
This commit is contained in:
parent
63ab8998d4
commit
0e37260a72
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -23,3 +30,4 @@ struct Array<T> {
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
9
examples/stdio.plsm
Normal file
9
examples/stdio.plsm
Normal 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
10
examples/test.plsm
Normal file
@ -0,0 +1,10 @@
|
||||
module test;
|
||||
|
||||
import std/list;
|
||||
|
||||
let glob = 10;
|
||||
|
||||
fun main() i32 {
|
||||
let local = glob + 10;
|
||||
ret local;
|
||||
}
|
@ -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
238
src/frontend/analysis.ts
Normal 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());
|
||||
});
|
||||
}
|
@ -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); }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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
49
src/frontend/modules.ts
Normal 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;
|
||||
}
|
@ -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(); }
|
||||
// }
|
||||
|
@ -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()]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
10
src/main.ts
10
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());
|
||||
|
||||
performNameAndTypeAnalysis(module);
|
||||
printErrors();
|
||||
module.accept(new AST.BaseVisitor());
|
Loading…
x
Reference in New Issue
Block a user