@@ -184,8 +184,13 @@ fn is_source_newer(source *Path, output *Path) std::io::Result<bool> {
|
||||
Ok(source_time > output_time)
|
||||
}
|
||||
|
||||
pub enum TranspileError<'a> {
|
||||
Parse[error::ParseError<'a, Vec<mist_parser::ast::TopLevel>>],
|
||||
Semantic[Vec<mist_parser::semantics::SemanticError>]
|
||||
}
|
||||
|
||||
pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf, mod_decl *str, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>)
|
||||
Result<[PathBuf, mist_codegen::RustCodegen, String], error::ParseError<'a, Vec<mist_parser::ast::TopLevel>>> {
|
||||
Result<[PathBuf, mist_codegen::RustCodegen, String], TranspileError<'a>> {
|
||||
|
||||
if let Some[[_, name]] = &curr_mod_decl {
|
||||
if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") {
|
||||
@@ -204,7 +209,11 @@ pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf
|
||||
|
||||
let mut gc = mist_codegen::RustCodegen::new(path.clone());
|
||||
|
||||
let parsed = mist_parser::parse(source)?;
|
||||
let parsed = mist_parser::parse(source).map_err(TranspileError::Parse)?;
|
||||
|
||||
for i in &parsed {
|
||||
let _ = mist_parser::semantics::check_class_semantics(i).map_err(TranspileError::Semantic)?;
|
||||
}
|
||||
|
||||
let output = gc.generate(parsed);
|
||||
|
||||
@@ -217,33 +226,9 @@ pub fn transpile_file(path *PathBuf, source *str, output_file PathBuf, mod_decl
|
||||
let [output_file, gc, output] = match transpile_code(path, source, output_file, mod_decl, curr_mod_decl) {
|
||||
Ok[output] => output;
|
||||
Err[e] => {
|
||||
match e {
|
||||
error::ParseError::Ast[e] => {
|
||||
let start_pos = e.span.start_pos().line_col();
|
||||
|
||||
let span = e.span.as_str();
|
||||
|
||||
eprintln!(
|
||||
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}",
|
||||
path.as_os_str().display(),
|
||||
start_pos.0,
|
||||
start_pos.1,
|
||||
e.error_message,
|
||||
span,
|
||||
if span.ends_with("\n") { "" } else { "\n" },
|
||||
"^".repeat(span.trim().len()),
|
||||
);
|
||||
|
||||
decode_err(&e, path);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
error::ParseError::PreAst[e] => {
|
||||
eprintln!("error: parse failed in {}\n{}", path.display(), e);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
||||
@@ -272,3 +257,46 @@ pub fn transpile_file(path *PathBuf, source *str, output_file PathBuf, mod_decl
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_err<'a>(err *TranspileError<'a>, path *PathBuf) {
|
||||
let curr_dir = std::env::current_dir().unwrap();
|
||||
|
||||
match err {
|
||||
TranspileError::Semantic[e_list] => {
|
||||
for e in e_list {
|
||||
eprintln!(
|
||||
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}",
|
||||
path.strip_prefix(&curr_dir).unwrap_or_else(|_| path).as_os_str().display(),
|
||||
e.line,
|
||||
e.column,
|
||||
e.error_message,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TranspileError::Parse[e] => {
|
||||
match e {
|
||||
error::ParseError::Ast[e] => {
|
||||
let start_pos = e.span.start_pos().line_col();
|
||||
|
||||
let span = e.span.as_str();
|
||||
|
||||
eprintln!(
|
||||
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}",
|
||||
path.strip_prefix(curr_dir).unwrap_or_else(|_| path).as_os_str().display(),
|
||||
start_pos.0,
|
||||
start_pos.1,
|
||||
e.error_message,
|
||||
span,
|
||||
if span.ends_with("\n") { "" } else { "\n" },
|
||||
"^".repeat(span.trim().len()),
|
||||
);
|
||||
}
|
||||
|
||||
error::ParseError::PreAst[e] => {
|
||||
eprintln!("error: parse failed in {}\n{}", path.strip_prefix(curr_dir).unwrap_or_else(|_| path).display(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ trait_decl = {
|
||||
override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? }
|
||||
|
||||
function_decl = {
|
||||
visibility? ~ override_kw? ~ "fn" ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ (block | semicolon)
|
||||
visibility? ~ "fn" ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ override_kw? ~ (block | semicolon)
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod ast;
|
||||
pub mod error;
|
||||
pub mod parser;
|
||||
pub mod rev_mapper;
|
||||
pub mod semantics;
|
||||
|
||||
use ast::*;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
|
||||
ast_ensure!(pair, Rule::function_decl => {
|
||||
let mut inner = pair.into_inner();
|
||||
let visibility = Visibility::try_from(&mut inner);
|
||||
let is_override = consume_rule(&mut inner, Rule::override_kw).map(Override::try_from).transpose();
|
||||
|
||||
let return_type = consume_rule(&mut inner, Rule::type_expr)
|
||||
.map(TypeExpr::try_from)
|
||||
@@ -64,6 +63,8 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
|
||||
})
|
||||
.unwrap_or_else(|| Ok(ParamList(self_param.into_iter().collect())));
|
||||
|
||||
let is_override = consume_rule(&mut inner, Rule::override_kw).map(Override::try_from).transpose();
|
||||
|
||||
let body = inner.next().map(Block::try_from).transpose();
|
||||
|
||||
ast_expr!(Self {
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
use crate::ast::{
|
||||
Block, ClassItem, Expression, FunctionDecl, Identifier, MatchItem, Postfix, Prefix, Spanned,
|
||||
Statement, StatementBranch, TopLevel, TopLevelKind, VarDeclStmt,
|
||||
};
|
||||
|
||||
pub trait GetMutability {
|
||||
fn get_mutability(&self) -> Vec<Identifier>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SemanticError {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
/// Class semantics - Ensuring fields are initialized
|
||||
|
||||
pub fn find_function_call<'a>(
|
||||
items: &'a Vec<ClassItem>,
|
||||
name: &Identifier,
|
||||
) -> Option<&'a Spanned<FunctionDecl>> {
|
||||
items.iter().find_map(|v| {
|
||||
if let ClassItem::Method(m) = v {
|
||||
if m.item.is_using_self() && &m.item.name == name {
|
||||
Some(m)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec<SemanticError>> {
|
||||
if let TopLevelKind::ClassDecl {
|
||||
fields,
|
||||
constructor,
|
||||
items,
|
||||
name,
|
||||
inherits,
|
||||
..
|
||||
} = &top_level.0.item
|
||||
{
|
||||
if let Some(constructor) = constructor {
|
||||
let mut fields = fields
|
||||
.iter()
|
||||
.map(|field| Spanned {
|
||||
item: field.item.decl.name.clone(),
|
||||
line: field.line,
|
||||
column: field.column,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if inherits.is_some() {
|
||||
fields.push(Spanned {
|
||||
line: top_level.0.line,
|
||||
column: top_level.0.column,
|
||||
item: Identifier("_super".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
let mut mutability = constructor.item.body.get_mutability();
|
||||
|
||||
let mut i = 0;
|
||||
while i < mutability.len() {
|
||||
let name = mutability[i].clone();
|
||||
if let Some(method) = find_function_call(items, &name) {
|
||||
if let Some(body) = &method.item.body {
|
||||
for entry in body.get_mutability() {
|
||||
if !mutability.contains(&entry) {
|
||||
mutability.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut errs = Vec::new();
|
||||
|
||||
for field in &fields {
|
||||
if !mutability.contains(&field.item) {
|
||||
errs.push(SemanticError {
|
||||
line: field.line,
|
||||
column: field.column,
|
||||
error_message: format!(
|
||||
"class field `{}.{}` is uninitialized",
|
||||
name.0, field.item.0
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if errs.len() > 0 {
|
||||
return Err(errs);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetMutability> GetMutability for Vec<T> {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
self.iter().flat_map(T::get_mutability).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for Block {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
let mut items: Vec<Identifier> = self
|
||||
.statements
|
||||
.iter()
|
||||
.flat_map(|v| v.item.get_mutability())
|
||||
.collect();
|
||||
|
||||
if let Some(v) = &self.soft_return {
|
||||
items.append(&mut v.item.get_mutability());
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for Statement {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
match self {
|
||||
Statement::Block(body) | Statement::UnsafeBlock(body) => body.get_mutability(),
|
||||
Statement::If {
|
||||
initial,
|
||||
else_if,
|
||||
else_branch,
|
||||
} => {
|
||||
let mut all_sets: Vec<Vec<Identifier>> = Vec::new();
|
||||
all_sets.push(initial.body.get_mutability());
|
||||
|
||||
for branch in else_if {
|
||||
all_sets.push(branch.body.get_mutability());
|
||||
}
|
||||
|
||||
if let Some(else_block) = else_branch {
|
||||
all_sets.push(else_block.get_mutability());
|
||||
} else {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut result = all_sets.remove(0);
|
||||
for set in all_sets {
|
||||
result.retain(|id| set.contains(id));
|
||||
}
|
||||
result
|
||||
}
|
||||
Statement::Loop(body) => body.get_mutability(),
|
||||
Statement::While(branch) => branch.body.get_mutability(),
|
||||
Statement::CStyleFor {
|
||||
body,
|
||||
init,
|
||||
condition,
|
||||
update,
|
||||
..
|
||||
} => {
|
||||
let mut a = init.get_mutability();
|
||||
a.append(&mut condition.get_mutability());
|
||||
a.append(&mut update.get_mutability());
|
||||
a.append(&mut body.get_mutability());
|
||||
a
|
||||
}
|
||||
Statement::For { body, iterator, .. } => {
|
||||
let mut a = iterator.get_mutability();
|
||||
a.append(&mut body.get_mutability());
|
||||
a
|
||||
}
|
||||
Statement::Match(_, items) => {
|
||||
if items.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let mut all_sets: Vec<Vec<Identifier>> = items
|
||||
.iter()
|
||||
.map(|item| item.item.get_mutability())
|
||||
.collect();
|
||||
let mut result = all_sets.remove(0);
|
||||
for set in all_sets {
|
||||
result.retain(|id| set.contains(id));
|
||||
}
|
||||
result
|
||||
}
|
||||
Statement::VarDecl(stmt) => stmt
|
||||
.init
|
||||
.as_ref()
|
||||
.map(|e| e.get_mutability())
|
||||
.unwrap_or_default(),
|
||||
Statement::Return(expr) => expr
|
||||
.as_ref()
|
||||
.map(|e| e.get_mutability())
|
||||
.unwrap_or_default(),
|
||||
Statement::Break | Statement::Continue => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for StatementBranch {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
let mut a = self.condition.get_mutability();
|
||||
a.append(&mut self.body.get_mutability());
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for MatchItem {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
self.1.get_mutability()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for VarDeclStmt {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
self.init
|
||||
.as_ref()
|
||||
.map(|e| e.get_mutability())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
||||
match initial {
|
||||
Expression::Path(v) => {
|
||||
let ident = v.0.first()?.ident.0.as_str();
|
||||
if ident == "self" || ident == "super" {
|
||||
if let Some(Postfix::FieldAccess(field, _)) = postfixes.first() {
|
||||
if !matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
||||
return Some(field.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_self_method_call(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
||||
match initial {
|
||||
Expression::Path(v) => {
|
||||
let ident = v.0.first()?.ident.0.as_str();
|
||||
if ident == "self" || ident == "super" {
|
||||
if let Some(Postfix::FieldAccess(method, _)) = postfixes.first() {
|
||||
if matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
||||
return Some(method.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn get_self_ref(&self) -> Option<Identifier> {
|
||||
match self {
|
||||
Self::Fix {
|
||||
initial, postfixes, ..
|
||||
} => get_self_ref(initial, postfixes),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetMutability for Expression {
|
||||
fn get_mutability(&self) -> Vec<Identifier> {
|
||||
match self {
|
||||
Self::Array(v) => v.get_mutability(),
|
||||
Self::ArrayRepeat(v, i) => {
|
||||
let mut a = v.get_mutability();
|
||||
a.append(&mut i.get_mutability());
|
||||
a
|
||||
}
|
||||
Self::Binary { lhs, op, rhs } => {
|
||||
if mutates_lhs(op) {
|
||||
let mut a = if let Some(self_ref) = lhs.get_self_ref() {
|
||||
vec![self_ref]
|
||||
} else {
|
||||
lhs.get_mutability()
|
||||
};
|
||||
|
||||
a.append(&mut rhs.get_mutability());
|
||||
a
|
||||
} else {
|
||||
let mut a = lhs.get_mutability();
|
||||
a.append(&mut rhs.get_mutability());
|
||||
a
|
||||
}
|
||||
}
|
||||
Self::Statement(stmt) => stmt.get_mutability(),
|
||||
Self::Fix {
|
||||
initial,
|
||||
prefixes,
|
||||
postfixes,
|
||||
} => {
|
||||
let mut result = Vec::new();
|
||||
|
||||
// &mut self.field / &mut super.field / &mut super pattern
|
||||
if prefixes.iter().any(|p| matches!(p, Prefix::RefMut)) {
|
||||
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
||||
result.push(self_ref);
|
||||
} else if let Expression::Path(v) = &**initial {
|
||||
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
||||
result.push(Identifier("_super".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse into all postfix sub-expressions
|
||||
for postfix in postfixes {
|
||||
match postfix {
|
||||
Postfix::Assign(_, rhs) => {
|
||||
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
||||
result.push(self_ref);
|
||||
}
|
||||
result.append(&mut rhs.get_mutability());
|
||||
}
|
||||
Postfix::Call(args) => {
|
||||
for arg in args {
|
||||
result.append(&mut arg.get_mutability());
|
||||
}
|
||||
}
|
||||
Postfix::StructCall(fields) => {
|
||||
for (_, expr) in fields {
|
||||
if let Some(expr) = expr {
|
||||
result.append(&mut expr.get_mutability());
|
||||
}
|
||||
}
|
||||
}
|
||||
Postfix::Index(expr) => {
|
||||
result.append(&mut expr.get_mutability());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Method calls on self: self.method_name(args)
|
||||
if let Some(method) = get_self_method_call(initial, postfixes) {
|
||||
result.push(method);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
Self::Closure { body, .. } => body.get_mutability(),
|
||||
Self::Literal(_) => Vec::new(),
|
||||
Self::Path(v) => {
|
||||
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
||||
vec![Identifier("_super".to_string())]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutates_lhs(op: &str) -> bool {
|
||||
matches!(op, "=" | "->")
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
pub module class_test;
|
||||
|
||||
pub class Animal<T> {
|
||||
pub class Animal {
|
||||
pub name String;
|
||||
pub val T;
|
||||
|
||||
constructor() {
|
||||
self.name = "Rex".to_string();
|
||||
@@ -19,7 +18,7 @@ pub class Animal<T> {
|
||||
pub fn void static_method() {}
|
||||
}
|
||||
|
||||
pub class Mammal<T> : Animal<T> {
|
||||
pub class Mammal : Animal {
|
||||
constructor() {
|
||||
super = Super::new();
|
||||
}
|
||||
@@ -30,12 +29,12 @@ pub class Mammal<T> : Animal<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub class Dog : Mammal<i32> {
|
||||
pub class Dog : Mammal {
|
||||
constructor() {
|
||||
super = Super::new();
|
||||
}
|
||||
|
||||
pub override(Animal<i32>) fn String speak(*self) {
|
||||
pub fn String speak(*self) override(Animal) {
|
||||
"Woof!".to_string()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user