Implement import-pass
This commit is contained in:
parent
464156b2dc
commit
a71843dfe9
@ -1,12 +1,14 @@
|
||||
use std::{env, fs};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
use reid::compile;
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if let Some(filename) = args.get(1) {
|
||||
let text = fs::read_to_string(filename)?;
|
||||
let output = match compile(&text) {
|
||||
let path = PathBuf::from(filename);
|
||||
|
||||
let text = fs::read_to_string(&path)?;
|
||||
let output = match compile(&text, PathBuf::from(path)) {
|
||||
Ok(t) => t,
|
||||
Err(e) => panic!("{}", e),
|
||||
};
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use reid::mir::{self, *};
|
||||
use reid_lib::Context;
|
||||
|
||||
@ -159,7 +161,9 @@ fn main() {
|
||||
name: "test module".to_owned(),
|
||||
imports: vec![],
|
||||
functions: vec![fibonacci, main],
|
||||
path: None,
|
||||
}],
|
||||
base: PathBuf::new(),
|
||||
};
|
||||
println!("test1");
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! This is the module that contains relevant code to parsing Reid, that is to
|
||||
//! say transforming a Vec of FullTokens into a loose parsed AST that can be
|
||||
//! used for unwrapping syntax sugar, and then be transformed into Reid MIR.
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::token_stream::TokenRange;
|
||||
|
||||
pub mod parse;
|
||||
@ -166,4 +168,5 @@ pub enum TopLevelStatement {
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
pub top_level_statements: Vec<TopLevelStatement>,
|
||||
pub path: Option<PathBuf>,
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{
|
||||
ast::{self},
|
||||
mir::{self, NamedVariableRef, StmtKind},
|
||||
};
|
||||
|
||||
impl mir::Context {
|
||||
pub fn from(modules: Vec<ast::Module>) -> mir::Context {
|
||||
mir::Context {
|
||||
modules: modules.iter().map(|m| m.process()).collect(),
|
||||
}
|
||||
pub fn from(modules: Vec<mir::Module>, base: PathBuf) -> mir::Context {
|
||||
mir::Context { modules, base }
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Module {
|
||||
fn process(&self) -> mir::Module {
|
||||
pub fn process(&self) -> mir::Module {
|
||||
let mut imports = Vec::new();
|
||||
let mut functions = Vec::new();
|
||||
|
||||
@ -67,6 +67,7 @@ impl ast::Module {
|
||||
name: self.name.clone(),
|
||||
imports,
|
||||
functions,
|
||||
path: self.path.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
pub enum Error {
|
||||
#[error("Invalid token '{}' at Ln {}, Col {}", .0, (.1).1, (.1).0)]
|
||||
InvalidToken(char, Position),
|
||||
|
@ -40,6 +40,8 @@
|
||||
//! - Loops
|
||||
//! ```
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use mir::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs};
|
||||
use reid_lib::Context;
|
||||
|
||||
@ -53,7 +55,7 @@ mod pad_adapter;
|
||||
mod token_stream;
|
||||
mod util;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
pub enum ReidError {
|
||||
#[error(transparent)]
|
||||
LexerError(#[from] lexer::Error),
|
||||
@ -63,10 +65,7 @@ pub enum ReidError {
|
||||
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
||||
}
|
||||
|
||||
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
|
||||
/// and `hello.asm` from it, which can be linked using `ld` to produce an
|
||||
/// executable file.
|
||||
pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||
pub fn compile_module(source: &str, path: Option<PathBuf>) -> Result<mir::Module, ReidError> {
|
||||
let tokens = lexer::tokenize(source)?;
|
||||
|
||||
dbg!(&tokens);
|
||||
@ -83,10 +82,20 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||
let ast_module = ast::Module {
|
||||
name: "test".to_owned(),
|
||||
top_level_statements: statements,
|
||||
path,
|
||||
};
|
||||
|
||||
dbg!(&ast_module);
|
||||
let mut mir_context = mir::Context::from(vec![ast_module]);
|
||||
Ok(ast_module.process())
|
||||
}
|
||||
|
||||
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
|
||||
/// and `hello.asm` from it, which can be linked using `ld` to produce an
|
||||
/// executable file.
|
||||
pub fn compile(source: &str, path: PathBuf) -> Result<String, ReidError> {
|
||||
let mut mir_context = mir::Context::from(
|
||||
vec![compile_module(source, Some(path.clone()))?],
|
||||
path.parent().unwrap().to_owned(),
|
||||
);
|
||||
|
||||
println!("{}", &mir_context);
|
||||
|
||||
|
120
reid/src/mir/imports.rs
Normal file
120
reid/src/mir/imports.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use std::{collections::HashMap, convert::Infallible, fs, path::PathBuf};
|
||||
|
||||
use crate::{compile_module, ReidError};
|
||||
|
||||
use super::{
|
||||
pass::{Pass, PassState},
|
||||
types::EqualsIssue,
|
||||
Context, FunctionDefinition, Import, Module,
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
pub enum ErrorKind {
|
||||
#[error("Unable to import inner modules, not yet supported: {0}")]
|
||||
InnerModulesNotYetSupported(Import),
|
||||
#[error("No such module: {0}")]
|
||||
ModuleNotFound(String),
|
||||
#[error("Error while compiling module {0}: {1}")]
|
||||
ModuleCompilationError(String, ReidError),
|
||||
#[error("No such function {0} found in module {1}")]
|
||||
NoSuchFunctionInModule(String, String),
|
||||
#[error("Importing function {0}::{1} not possible: {2}")]
|
||||
FunctionImportIssue(String, String, EqualsIssue),
|
||||
}
|
||||
|
||||
/// Struct used to implement a type-checking pass that can be performed on the
|
||||
/// MIR.
|
||||
pub struct ImportsPass {}
|
||||
|
||||
impl Pass for ImportsPass {
|
||||
type TError = ErrorKind;
|
||||
fn context(&mut self, context: &mut Context, mut state: PassState<Self::TError>) {
|
||||
let mut modules = HashMap::<String, Module>::new();
|
||||
|
||||
for module in context.modules.clone() {
|
||||
modules.insert(module.name.clone(), module);
|
||||
}
|
||||
|
||||
let mut modules_to_process: Vec<_> = modules.values().cloned().collect();
|
||||
|
||||
let iter = modules_to_process.iter_mut();
|
||||
|
||||
for module in iter {
|
||||
let mut new_modules = Vec::new();
|
||||
|
||||
for import in &module.imports {
|
||||
let Import(path, _) = import;
|
||||
if path.len() != 2 {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::InnerModulesNotYetSupported(import.clone())),
|
||||
import.1,
|
||||
);
|
||||
}
|
||||
|
||||
let module_name = unsafe { path.get_unchecked(0) };
|
||||
|
||||
let imported = if let Some(module) = modules.get(module_name) {
|
||||
module
|
||||
} else {
|
||||
let file_path = PathBuf::from(&context.base.clone()).join(module_name);
|
||||
|
||||
let Ok(source) = fs::read_to_string(&file_path) else {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::ModuleNotFound(module_name.clone())),
|
||||
import.1,
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
match compile_module(&source, Some(file_path)) {
|
||||
Ok(m) => {
|
||||
new_modules.push(m);
|
||||
new_modules.last().unwrap()
|
||||
}
|
||||
Err(err) => {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::ModuleCompilationError(module_name.clone(), err)),
|
||||
import.1,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let func_name = unsafe { path.get_unchecked(1) };
|
||||
|
||||
let Some(func) = imported.functions.iter().find(|f| f.name == *func_name) else {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::NoSuchFunctionInModule(
|
||||
module_name.clone(),
|
||||
func_name.clone(),
|
||||
)),
|
||||
import.1,
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(existing) = module.functions.iter().find(|f| f.name == *func_name) {
|
||||
if let Err(e) = existing.equals_as_imported(func) {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::FunctionImportIssue(
|
||||
module_name.clone(),
|
||||
func_name.clone(),
|
||||
e,
|
||||
)),
|
||||
import.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.functions.push(FunctionDefinition {
|
||||
name: func.name.clone(),
|
||||
is_pub: false,
|
||||
return_type: func.return_type.clone(),
|
||||
parameters: func.parameters.clone(),
|
||||
kind: super::FunctionDefinitionKind::Extern,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,12 @@
|
||||
//! Reid. It contains a simplified version of Reid which can be e.g.
|
||||
//! typechecked.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::token_stream::TokenRange;
|
||||
|
||||
mod display;
|
||||
pub mod imports;
|
||||
pub mod pass;
|
||||
pub mod typecheck;
|
||||
pub mod typeinference;
|
||||
@ -205,13 +208,13 @@ pub enum ReturnKind {
|
||||
Soft,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import(pub Vec<String>, pub Metadata);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExprKind {
|
||||
Variable(NamedVariableRef),
|
||||
Index(Box<Expression>, TypeKind, u64),
|
||||
@ -223,21 +226,21 @@ pub enum ExprKind {
|
||||
Block(Block),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expression(pub ExprKind, pub Metadata);
|
||||
|
||||
/// Condition, Then, Else
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfExpression(pub Box<Expression>, pub Block, pub Option<Block>);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionCall {
|
||||
pub name: String,
|
||||
pub return_type: TypeKind,
|
||||
pub parameters: Vec<Expression>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionDefinition {
|
||||
pub name: String,
|
||||
pub is_pub: bool,
|
||||
@ -246,7 +249,7 @@ pub struct FunctionDefinition {
|
||||
pub kind: FunctionDefinitionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FunctionDefinitionKind {
|
||||
/// Actual definition block and surrounding signature range
|
||||
Local(Block, Metadata),
|
||||
@ -269,7 +272,7 @@ impl FunctionDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block {
|
||||
/// List of non-returning statements
|
||||
pub statements: Vec<Statement>,
|
||||
@ -277,22 +280,22 @@ pub struct Block {
|
||||
pub meta: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Statement(pub StmtKind, pub Metadata);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IndexedVariableReference {
|
||||
pub kind: IndexedVariableReferenceKind,
|
||||
pub meta: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IndexedVariableReferenceKind {
|
||||
Named(NamedVariableRef),
|
||||
Index(Box<IndexedVariableReference>, u64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StmtKind {
|
||||
/// Variable name++mutability+type, evaluation
|
||||
Let(NamedVariableRef, bool, Expression),
|
||||
@ -301,14 +304,16 @@ pub enum StmtKind {
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
pub imports: Vec<Import>,
|
||||
pub functions: Vec<FunctionDefinition>,
|
||||
pub path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
pub modules: Vec<Module>,
|
||||
pub base: PathBuf,
|
||||
}
|
||||
|
@ -191,6 +191,7 @@ impl<'st, 'sc, TError: STDError + Clone> PassState<'st, 'sc, TError> {
|
||||
pub trait Pass {
|
||||
type TError: STDError + Clone;
|
||||
|
||||
fn context(&mut self, _context: &mut Context, mut _state: PassState<Self::TError>) {}
|
||||
fn module(&mut self, _module: &mut Module, mut _state: PassState<Self::TError>) {}
|
||||
fn function(
|
||||
&mut self,
|
||||
@ -207,6 +208,7 @@ impl Context {
|
||||
pub fn pass<T: Pass>(&mut self, pass: &mut T) -> State<T::TError> {
|
||||
let mut state = State::new();
|
||||
let mut scope = Scope::default();
|
||||
pass.context(self, PassState::from(&mut state, &mut scope));
|
||||
for module in &mut self.modules {
|
||||
module.pass(pass, &mut state, &mut scope);
|
||||
}
|
||||
|
@ -180,3 +180,34 @@ impl IndexedVariableReference {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
||||
pub enum EqualsIssue {
|
||||
#[error("Function is already defined locally at {:?}", (.0).range)]
|
||||
ExistsLocally(Metadata),
|
||||
#[error("asd")]
|
||||
Equals,
|
||||
#[error("asd")]
|
||||
ConflictingImports,
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
pub fn equals_as_imported(&self, other: &FunctionDefinition) -> Result<(), EqualsIssue> {
|
||||
match &self.kind {
|
||||
FunctionDefinitionKind::Local(_, metadata) => {
|
||||
Err(EqualsIssue::ExistsLocally(*metadata))
|
||||
}
|
||||
FunctionDefinitionKind::Extern => {
|
||||
if self.is_pub == other.is_pub
|
||||
&& self.name == other.name
|
||||
&& self.parameters == other.parameters
|
||||
&& self.return_type == other.return_type
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EqualsIssue::ConflictingImports)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ impl std::iter::Sum for TokenRange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
pub enum Error {
|
||||
#[error("Expected {} at Ln {}, Col {}, got {:?}", .0, (.2).1, (.2).0, .1)]
|
||||
Expected(String, Token, Position),
|
||||
|
Loading…
Reference in New Issue
Block a user