general update
This commit is contained in:
parent
63ab8998d4
commit
0e37260a72
@ -1,22 +1,29 @@
|
|||||||
module test;
|
module std/list;
|
||||||
|
|
||||||
import stdlib/io;
|
import std/io;
|
||||||
import stdlib/heap;
|
import std/heap;
|
||||||
import stdlib/err;
|
|
||||||
|
|
||||||
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;
|
pub trait Collection<T> {
|
||||||
isNotEmpty() -> bool = self.size() <= 0;
|
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;
|
let mut res = false;
|
||||||
|
|
||||||
self.each((item, break) -> {
|
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;
|
let mut res = true;
|
||||||
|
|
||||||
self.each((item, break) -> {
|
self.each((item, break) -> {
|
||||||
@ -39,32 +46,25 @@ trait Collection<T> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
ret res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// map<S>(fn: (&T) -> &S) -> Collection<S>;
|
// map<S>(fn: (&T) -> &S) -> Collection<S>;
|
||||||
// filter(fn: (&T) -> bool) -> Collection<T>;
|
// 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> {
|
trait Stack<T> : Collection<T> {
|
||||||
push(item: &T) -> void;
|
push(item: &T) void;
|
||||||
pop() -> &T;
|
pop() &T;
|
||||||
peek() -> &T?;
|
peek() &T?;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LinkedList<T> {
|
pub struct LinkedList<T> {
|
||||||
pub mut head: &T?;
|
pub mut head: &T?;
|
||||||
pub mut tail: &LinkedList<T>?;
|
pub mut tail: &LinkedList<T>?;
|
||||||
mut size: u64;
|
mut size: u64;
|
||||||
|
|
||||||
init() -> void {
|
init() void {
|
||||||
self.head = null;
|
self.head = null;
|
||||||
self.tail = null;
|
self.tail = null;
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
@ -72,11 +72,11 @@ struct LinkedList<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Collection<T> for LinkedList<T> {
|
impl Collection<T> for LinkedList<T> {
|
||||||
add(item: &T) -> void {
|
add(item: &T) void {
|
||||||
if self.head == null {
|
if self.head == null {
|
||||||
self.head = item;
|
self.head = item;
|
||||||
self.size = self.size + 1;
|
self.size = self.size + 1;
|
||||||
return;
|
ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tail == null {
|
if self.tail == null {
|
||||||
@ -88,9 +88,9 @@ impl Collection<T> for LinkedList<T> {
|
|||||||
self.size = self.size + 1;
|
self.size = self.size + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(item: &T) -> void {
|
remove(item: &T) void {
|
||||||
if self.head == null {
|
if self.head == null {
|
||||||
return;
|
ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.head == item {
|
if self.head == item {
|
||||||
@ -104,36 +104,36 @@ impl Collection<T> for LinkedList<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.size = self.size - 1;
|
self.size = self.size - 1;
|
||||||
return;
|
ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tail.remove(item);
|
self.tail.remove(item);
|
||||||
self.size = self.size - 1;
|
self.size = self.size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(item: &T) -> bool {
|
contains(item: &T) bool {
|
||||||
if self.head == null {
|
if self.head == null {
|
||||||
return false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.head == item {
|
if self.head == item {
|
||||||
return true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.tail.contains(item);
|
ret self.tail.contains(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
size() -> u64 {
|
size() u64 {
|
||||||
return self.size;
|
ret self.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
each(fn: (item: &T, break: () -> void) -> void) -> void {
|
each(fn: (item: &T, break: () -> void) -> void) void {
|
||||||
if self.head == null {
|
if self.head == null {
|
||||||
return;
|
ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut continue = true;
|
let mut continue = true;
|
||||||
fn(self.head, () -> { continue = false; });
|
fn(self.head, () -> continue = false);
|
||||||
|
|
||||||
if continue && self.tail != null {
|
if continue && self.tail != null {
|
||||||
self.tail.each(fn);
|
self.tail.each(fn);
|
||||||
@ -142,28 +142,28 @@ impl Collection<T> for LinkedList<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Queue<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 {
|
if head == null {
|
||||||
// TODO: wait for item to be available
|
// TODO: wait for item to be available
|
||||||
return null;
|
ret null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = self.head;
|
let item = self.head;
|
||||||
self.remove(item);
|
self.remove(item);
|
||||||
return item;
|
ret item;
|
||||||
}
|
}
|
||||||
|
|
||||||
poll() -> &T? {
|
poll() &T? {
|
||||||
if self.head == null {
|
if self.head == null {
|
||||||
return null;
|
ret null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = self.head;
|
let item = self.head;
|
||||||
self.remove(item);
|
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) {
|
fun alloc(size: u64) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun new(_struct: Struct) {
|
fun new(_struct: Struct) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
/*
|
||||||
|
fun memset(addr: &u8, count: u64, value: u8) {
|
||||||
unsafe fun memset(addr: &u8, count: u64, value: u8) {
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
addr[i] = value;
|
addr[i] = value;
|
||||||
@ -18,8 +25,9 @@ unsafe fun memset(addr: &u8, count: u64, value: u8) {
|
|||||||
|
|
||||||
struct Array<T> {
|
struct Array<T> {
|
||||||
ptr: &T;
|
ptr: &T;
|
||||||
|
|
||||||
([])(index: u64) {
|
([])(index: u64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
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(
|
$ast = new AST.ImportStmt(
|
||||||
this.getSourceRange($ctx),
|
this.getSourceRange($ctx),
|
||||||
$qualifiers.map(q => q.text),
|
$qualifiers.map(q => q.text),
|
||||||
|
$alias.text ?? null,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ funDecl
|
|||||||
returns[AST.FunDecl ast = null as any]:
|
returns[AST.FunDecl ast = null as any]:
|
||||||
pub = 'pub'? 'fun' name = ID (
|
pub = 'pub'? 'fun' name = ID (
|
||||||
'(' (params += funParam (',' params += funParam)* ','?)? ')'
|
'(' (params += funParam (',' params += funParam)* ','?)? ')'
|
||||||
)? '->' returnTypeName = typeName '->' (
|
)? returnTypeName = typeName (
|
||||||
'=' exprBody = expr ';'
|
'=' exprBody = expr ';'
|
||||||
| blockBody = block
|
| blockBody = block
|
||||||
) {
|
) {
|
||||||
@ -125,10 +126,10 @@ funDecl
|
|||||||
|
|
||||||
funParam
|
funParam
|
||||||
returns[AST.FunParam ast = null as any]:
|
returns[AST.FunParam ast = null as any]:
|
||||||
name = ID ':' typeName {
|
(name = ID ':')? typeName {
|
||||||
$ast = new AST.FunParam(
|
$ast = new AST.FunParam(
|
||||||
this.getSourceRange($ctx),
|
this.getSourceRange($ctx),
|
||||||
$name.text!,
|
$ctx._name?.text ?? null,
|
||||||
$typeName.ast,
|
$typeName.ast,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -197,7 +198,7 @@ memberMethod
|
|||||||
returns[AST.MemberMethod ast = null as any]:
|
returns[AST.MemberMethod ast = null as any]:
|
||||||
pub = 'pub'? name = ID '(' (
|
pub = 'pub'? name = ID '(' (
|
||||||
params += funParam (',' params += funParam)* ','?
|
params += funParam (',' params += funParam)* ','?
|
||||||
)? ')' '->' returnTypeName = typeName (
|
)? ')' returnTypeName = typeName (
|
||||||
';'
|
';'
|
||||||
| blockBody = block
|
| blockBody = block
|
||||||
| '=' exprBody = expr ';'
|
| '=' exprBody = expr ';'
|
||||||
@ -220,7 +221,7 @@ memberMethod
|
|||||||
|
|
||||||
returnStmt
|
returnStmt
|
||||||
returns[AST.ReturnStmt ast = null as any]:
|
returns[AST.ReturnStmt ast = null as any]:
|
||||||
'return' expr? ';' {
|
'ret' expr? ';' {
|
||||||
$ast = new AST.ReturnStmt(
|
$ast = new AST.ReturnStmt(
|
||||||
this.getSourceRange($ctx),
|
this.getSourceRange($ctx),
|
||||||
$ctx.expr()?.ast ?? null,
|
$ctx.expr()?.ast ?? null,
|
||||||
@ -329,19 +330,21 @@ typeName
|
|||||||
|
|
||||||
namedTypeName
|
namedTypeName
|
||||||
returns[AST.NamedTypeName ast = null as any]:
|
returns[AST.NamedTypeName ast = null as any]:
|
||||||
name = ID {
|
name = qualifiedName {
|
||||||
$ast = new AST.NamedTypeName(
|
$ast = new AST.NamedTypeName(
|
||||||
this.getSourceRange($ctx),
|
this.getSourceRange($ctx),
|
||||||
$name.text!,
|
$name.name,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
genericTypeName
|
genericTypeName
|
||||||
returns[AST.GenericTypeName ast = null as any]:
|
returns[AST.GenericTypeName ast = null as any]:
|
||||||
name = ID '<' typeArgs += typeName (',' typeArgs += typeName)* '>' {
|
name = qualifiedName '<' typeArgs += typeName (
|
||||||
|
',' typeArgs += typeName
|
||||||
|
)* '>' {
|
||||||
$ast = new AST.GenericTypeName(
|
$ast = new AST.GenericTypeName(
|
||||||
this.getSourceRange($ctx),
|
this.getSourceRange($ctx),
|
||||||
$name.text!,
|
$name.name,
|
||||||
$typeArgs.map(arg => arg.ast),
|
$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 { Block } from "./block";
|
||||||
import type { Expr } from "./expr";
|
import type { Expr } from "./expr";
|
||||||
import { Node, SourceRange } from "./node";
|
import { Node, SourceRange } from "./node";
|
||||||
@ -39,7 +40,7 @@ export class FunDecl extends Decl {
|
|||||||
export class FunParam extends Node {
|
export class FunParam extends Node {
|
||||||
constructor(
|
constructor(
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
public name: string,
|
public name: string | null,
|
||||||
public typeName: TypeName,
|
public typeName: TypeName,
|
||||||
) { super(sourceRange); }
|
) { super(sourceRange); }
|
||||||
|
|
||||||
@ -49,12 +50,14 @@ export class FunParam extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TraitDecl extends Decl {
|
export class TraitDecl extends Decl {
|
||||||
|
public symbol: TraitSymbol | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
public isPub: boolean,
|
public isPub: boolean,
|
||||||
public name: string,
|
public name: string,
|
||||||
public genericParams: string[],
|
public genericParams: string[],
|
||||||
public parents: (NamedTypeName | GenericTypeName)[],
|
public parentTraitTypeNames: (NamedTypeName | GenericTypeName)[],
|
||||||
public methods: MemberMethod[],
|
public methods: MemberMethod[],
|
||||||
) { super(sourceRange); }
|
) { super(sourceRange); }
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
import type { Symbol } from "../semantics/symbol";
|
||||||
import type { Decl } from "./decl";
|
import type { Decl } from "./decl";
|
||||||
import { Node, SourceRange } from "./node";
|
import { Node, SourceRange } from "./node";
|
||||||
import { Stmt } from "./stmt";
|
import { Stmt } from "./stmt";
|
||||||
import type { Visitor } from "./visitor";
|
import type { Visitor } from "./visitor";
|
||||||
|
|
||||||
export class Module extends Node {
|
export class Module extends Node {
|
||||||
|
public symbols: Record<string, Symbol> = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
public moduleDecl: ModuleDecl,
|
public moduleDecl: ModuleDecl,
|
||||||
@ -14,6 +17,24 @@ export class Module extends Node {
|
|||||||
accept<ResultT>(visitor: Visitor<ResultT>): ResultT {
|
accept<ResultT>(visitor: Visitor<ResultT>): ResultT {
|
||||||
return visitor.visitModule(this);
|
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 {
|
export class ModuleDecl extends Stmt {
|
||||||
@ -27,7 +48,13 @@ export class ModuleDecl extends Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ImportStmt 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);
|
super(sourceRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ export abstract class TypeName extends Node { }
|
|||||||
export class NamedTypeName extends TypeName {
|
export class NamedTypeName extends TypeName {
|
||||||
constructor(
|
constructor(
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
public name: string,
|
public path: string[],
|
||||||
) { super(sourceRange); }
|
) { super(sourceRange); }
|
||||||
|
|
||||||
accept<ResultT>(visitor: Visitor<ResultT>): ResultT {
|
accept<ResultT>(visitor: Visitor<ResultT>): ResultT {
|
||||||
@ -17,7 +17,7 @@ export class NamedTypeName extends TypeName {
|
|||||||
export class GenericTypeName extends TypeName {
|
export class GenericTypeName extends TypeName {
|
||||||
constructor(
|
constructor(
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
public name: string,
|
public path: string[],
|
||||||
public args: TypeName[],
|
public args: TypeName[],
|
||||||
) { super(sourceRange); }
|
) { super(sourceRange); }
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ export abstract class Visitor<ResultT = any> {
|
|||||||
public abstract visitMemberField(memberField: MemberField): ResultT;
|
public abstract visitMemberField(memberField: MemberField): ResultT;
|
||||||
public abstract visitMemberMethod(memberMethod: MemberMethod): ResultT;
|
public abstract visitMemberMethod(memberMethod: MemberMethod): ResultT;
|
||||||
|
|
||||||
|
|
||||||
public abstract visitIdentifier(identifier: Identifier): ResultT;
|
public abstract visitIdentifier(identifier: Identifier): ResultT;
|
||||||
public abstract visitIntExpr(intExpr: IntExpr): ResultT;
|
public abstract visitIntExpr(intExpr: IntExpr): ResultT;
|
||||||
public abstract visitFloatExpr(floatExpr: FloatExpr): 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> {
|
export class BaseVisitor<ResultT = any> implements Visitor<ResultT> {
|
||||||
public visitModule(module_: Module): ResultT {
|
public visitModule(module_: Module): ResultT {
|
||||||
|
// console.log(module_.moduleDecl.path);
|
||||||
module_.imports.forEach((import_) => import_.accept(this));
|
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;
|
return null as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,11 +111,13 @@ export class BaseVisitor<ResultT = any> implements Visitor<ResultT> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public visitTraitDecl(traitDecl: TraitDecl): ResultT {
|
public visitTraitDecl(traitDecl: TraitDecl): ResultT {
|
||||||
|
traitDecl.parentTraitTypeNames.forEach((parent) => parent.accept(this));
|
||||||
traitDecl.methods.forEach((method) => method.accept(this));
|
traitDecl.methods.forEach((method) => method.accept(this));
|
||||||
return null as any;
|
return null as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
public visitImplDecl(implDecl: ImplDecl): ResultT {
|
public visitImplDecl(implDecl: ImplDecl): ResultT {
|
||||||
|
implDecl.traitTypeName.accept(this);
|
||||||
implDecl.methods.forEach((attr) => attr.accept(this));
|
implDecl.methods.forEach((attr) => attr.accept(this));
|
||||||
return null as any;
|
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(
|
constructor(
|
||||||
public name: string,
|
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(); }
|
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 {
|
export class StructType extends Type {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string,
|
public name: string,
|
||||||
public fields: [string, Type][],
|
public generics: [string, GenericType][] = [],
|
||||||
|
public fields: [string, Type][] = [],
|
||||||
|
public impls: TraitType[] = [],
|
||||||
) { super(); }
|
) { 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) {
|
public hasField(field: string) {
|
||||||
for (const [name] of this.fields) {
|
for (const [name] of this.fields) {
|
||||||
if (name === field) return true;
|
if (name === field) return true;
|
||||||
@ -35,11 +417,80 @@ export class StructType extends Type {
|
|||||||
|
|
||||||
throw new Error(`StructType.getFieldOffset(...): field '${field}' not found'`);
|
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 {
|
export class TraitType extends Type {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string,
|
public name: string,
|
||||||
public args: Type[],
|
public parents: TraitType[] = [],
|
||||||
|
public methods: [string, FunType][] = [],
|
||||||
|
public generics: [string, GenericType][] = [],
|
||||||
) { super(); }
|
) { 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()]),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
src/main.ts
12
src/main.ts
@ -2,9 +2,13 @@ import { inspect } from "bun";
|
|||||||
import { parseModule } from "./parser";
|
import { parseModule } from "./parser";
|
||||||
import { hasErrors, printErrors } from "./errors";
|
import { hasErrors, printErrors } from "./errors";
|
||||||
import AST from "./frontend/ast";
|
import AST from "./frontend/ast";
|
||||||
|
import { performNameAndTypeAnalysis } from "./frontend/analysis";
|
||||||
|
|
||||||
const module = parseModule('examples/LinkedList.plsm');
|
const module = parseModule('examples/test.plsm');
|
||||||
console.log(inspect(module, { depth: 1000, colors: false }));
|
// console.log(inspect(module, { depth: 1000, colors: false }));
|
||||||
|
|
||||||
printErrors();
|
// printErrors();
|
||||||
module.accept(new AST.BaseVisitor());
|
// module.accept(new AST.BaseVisitor());
|
||||||
|
|
||||||
|
performNameAndTypeAnalysis(module);
|
||||||
|
printErrors();
|
Loading…
x
Reference in New Issue
Block a user