mirror of
https://github.com/edg-l/edlang.git
synced 2024-09-07 19:35:16 +00:00
progress
This commit is contained in:
parent
57a5621961
commit
3f95947075
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.showUnlinkedFileNotification": false
|
||||
}
|
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -539,12 +539,14 @@ dependencies = [
|
|||
name = "edlang_codegen_mlir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"edlang_ast",
|
||||
"edlang_parser",
|
||||
"edlang_session",
|
||||
"llvm-sys",
|
||||
"melior",
|
||||
"mlir-sys",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -566,6 +568,7 @@ dependencies = [
|
|||
name = "edlang_parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"edlang_ast",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
|
@ -576,6 +579,9 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "edlang_session"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
|
|
|
@ -1 +1,190 @@
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||
pub struct Span {
|
||||
pub lo: usize,
|
||||
pub hi: usize,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(lo: usize, hi: usize) -> Self {
|
||||
Self { lo, hi }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Module {
|
||||
pub imports: Vec<Import>,
|
||||
pub contents: Vec<ModuleStatement>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum ModuleStatement {
|
||||
Function(Function),
|
||||
Constant(Constant),
|
||||
Struct(Struct),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Import {
|
||||
pub path: PathExpr,
|
||||
/// If symbols is empty then the last path ident is the symbol.
|
||||
pub symbols: Vec<Ident>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct PathExpr {
|
||||
pub first: Ident,
|
||||
pub extra: Vec<PathSegment>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum PathSegment {
|
||||
Field(Ident),
|
||||
Index { value: Expression, span: Span },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Ident {
|
||||
pub name: String,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// T: A + B
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Type {
|
||||
pub name: Ident,
|
||||
pub generics: Vec<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FnParam {
|
||||
pub name: Ident,
|
||||
pub arg_type: Type,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Block {
|
||||
pub body: Vec<Statement>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Statement {
|
||||
Let,
|
||||
Assign,
|
||||
For,
|
||||
While,
|
||||
If,
|
||||
Return,
|
||||
FnCall(FnCallExpr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Function {
|
||||
pub name: Ident,
|
||||
pub is_extern: bool,
|
||||
pub is_public: bool,
|
||||
pub params: Vec<FnParam>,
|
||||
pub return_type: Option<Type>,
|
||||
pub body: Block,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Constant {
|
||||
pub name: Ident,
|
||||
pub r#type: Type,
|
||||
pub value: Expression,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Field {
|
||||
pub name: Ident,
|
||||
pub r#type: Type,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Struct {
|
||||
pub name: Ident,
|
||||
pub generics: Vec<Type>,
|
||||
pub fields: Vec<Field>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Expression {
|
||||
Value(ValueExpr),
|
||||
FnCall(FnCallExpr),
|
||||
Unary(UnaryOp, Box<Self>),
|
||||
Binary(Box<Self>, BinaryOp, Box<Self>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum ValueExpr {
|
||||
Bool { value: bool, span: Span },
|
||||
Char { value: char, span: Span },
|
||||
Int { value: u128, span: Span },
|
||||
Float { value: String, span: Span },
|
||||
Str { value: String, span: Span },
|
||||
Path(PathExpr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FnCallExpr {
|
||||
pub name: Ident,
|
||||
pub generic_params: Vec<Type>,
|
||||
pub params: Vec<Expression>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum UnaryOp {
|
||||
ArithNeg(Span),
|
||||
LogicalNot(Span),
|
||||
BitwiseNot(Span),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum BinaryOp {
|
||||
Arith(ArithOp, Span),
|
||||
Logic(LogicOp, Span),
|
||||
Compare(CmpOp, Span),
|
||||
Bitwise(BitwiseOp, Span),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum ArithOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum LogicOp {
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum CmpOp {
|
||||
Eq,
|
||||
NotEq,
|
||||
Lt,
|
||||
LtEq,
|
||||
Gt,
|
||||
GtEq,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum BitwiseOp {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
}
|
||||
|
|
|
@ -17,3 +17,7 @@ edlang_session = { version = "0.1.0", path = "../edlang_session" }
|
|||
llvm-sys = "170.0.1"
|
||||
melior = { version = "0.15.0", features = ["ods-dialects"] }
|
||||
mlir-sys = "0.2.1"
|
||||
tracing = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.83"
|
||||
|
|
16
lib/edlang_codegen_mlir/build.rs
Normal file
16
lib/edlang_codegen_mlir/build.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use std::env::var;
|
||||
|
||||
fn main() {
|
||||
let mlir_path = var("MLIR_SYS_170_PREFIX").expect("MLIR path should be set.");
|
||||
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.flag("-std=c++17")
|
||||
.flag_if_supported("-Wno-unused-parameter")
|
||||
.flag_if_supported("-Wno-comment")
|
||||
.include(&format!("{mlir_path}/include"))
|
||||
.file("src/wrappers.cpp")
|
||||
.compile("ffi");
|
||||
|
||||
println!("cargo:rerun-if-changed=src/wrappers.cpp");
|
||||
}
|
1
lib/edlang_codegen_mlir/src/codegen.rs
Normal file
1
lib/edlang_codegen_mlir/src/codegen.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
10
lib/edlang_codegen_mlir/src/ffi.rs
Normal file
10
lib/edlang_codegen_mlir/src/ffi.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use llvm_sys::prelude::{LLVMContextRef, LLVMModuleRef};
|
||||
|
||||
extern "C" {
|
||||
/// Translate operation that satisfies LLVM dialect module requirements into an LLVM IR module living in the given context.
|
||||
/// This translates operations from any dilalect that has a registered implementation of LLVMTranslationDialectInterface.
|
||||
pub fn mlirTranslateModuleToLLVMIR(
|
||||
module_operation_ptr: mlir_sys::MlirOperation,
|
||||
llvm_context: LLVMContextRef,
|
||||
) -> LLVMModuleRef;
|
||||
}
|
|
@ -1 +1,153 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
mem::MaybeUninit,
|
||||
path::PathBuf,
|
||||
ptr::{addr_of_mut, null_mut},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use edlang_session::{OptLevel, Session};
|
||||
use llvm_sys::{
|
||||
core::{LLVMContextCreate, LLVMContextDispose, LLVMDisposeMessage, LLVMDisposeModule},
|
||||
target::{
|
||||
LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos, LLVM_InitializeAllTargetMCs,
|
||||
LLVM_InitializeAllTargets,
|
||||
},
|
||||
target_machine::{
|
||||
LLVMCodeGenFileType, LLVMCodeGenOptLevel, LLVMCodeModel, LLVMCreateTargetMachine,
|
||||
LLVMDisposeTargetMachine, LLVMGetDefaultTargetTriple, LLVMGetHostCPUFeatures,
|
||||
LLVMGetHostCPUName, LLVMGetTargetFromTriple, LLVMRelocMode, LLVMTargetMachineEmitToFile,
|
||||
LLVMTargetRef,
|
||||
},
|
||||
};
|
||||
use melior::ir::Module;
|
||||
|
||||
use crate::ffi::mlirTranslateModuleToLLVMIR;
|
||||
|
||||
pub mod codegen;
|
||||
mod ffi;
|
||||
pub mod linker;
|
||||
|
||||
/// Converts a module to an object.
|
||||
/// The object will be written to the specified target path.
|
||||
/// TODO: error handling
|
||||
///
|
||||
/// Returns the path to the object.
|
||||
pub fn compile_to_object(
|
||||
session: &Session,
|
||||
module: &Module,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
tracing::debug!("Compiling to object file");
|
||||
if !session.target_dir.exists() {
|
||||
std::fs::create_dir_all(&session.target_dir)?;
|
||||
}
|
||||
|
||||
let target_file = session
|
||||
.file_path
|
||||
.clone()
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let target_file = PathBuf::from(target_file).with_extension("o");
|
||||
tracing::debug!("Target file: {:?}", target_file);
|
||||
|
||||
let target_path = session.target_dir.join(target_file);
|
||||
|
||||
// TODO: Rework so you can specify target and host features, etc.
|
||||
// Right now it compiles for the native cpu feature set and arch.
|
||||
static INITIALIZED: OnceLock<()> = OnceLock::new();
|
||||
|
||||
INITIALIZED.get_or_init(|| unsafe {
|
||||
LLVM_InitializeAllTargets();
|
||||
LLVM_InitializeAllTargetInfos();
|
||||
LLVM_InitializeAllTargetMCs();
|
||||
LLVM_InitializeAllAsmPrinters();
|
||||
tracing::debug!("initialized llvm targets");
|
||||
});
|
||||
|
||||
unsafe {
|
||||
let llvm_context = LLVMContextCreate();
|
||||
|
||||
let op = module.as_operation().to_raw();
|
||||
|
||||
let llvm_module = mlirTranslateModuleToLLVMIR(op, llvm_context);
|
||||
|
||||
let mut null = null_mut();
|
||||
let mut error_buffer = addr_of_mut!(null);
|
||||
|
||||
let target_triple = LLVMGetDefaultTargetTriple();
|
||||
tracing::debug!("Target triple: {:?}", CStr::from_ptr(target_triple));
|
||||
|
||||
let target_cpu = LLVMGetHostCPUName();
|
||||
tracing::debug!("Target CPU: {:?}", CStr::from_ptr(target_cpu));
|
||||
|
||||
let target_cpu_features = LLVMGetHostCPUFeatures();
|
||||
tracing::debug!(
|
||||
"Target CPU Features: {:?}",
|
||||
CStr::from_ptr(target_cpu_features)
|
||||
);
|
||||
|
||||
let mut target: MaybeUninit<LLVMTargetRef> = MaybeUninit::uninit();
|
||||
|
||||
if LLVMGetTargetFromTriple(target_triple, target.as_mut_ptr(), error_buffer) != 0 {
|
||||
let error = CStr::from_ptr(*error_buffer);
|
||||
let err = error.to_string_lossy().to_string();
|
||||
tracing::error!("error getting target triple: {}", err);
|
||||
LLVMDisposeMessage(*error_buffer);
|
||||
panic!("{err}")
|
||||
} else if !(*error_buffer).is_null() {
|
||||
LLVMDisposeMessage(*error_buffer);
|
||||
error_buffer = addr_of_mut!(null);
|
||||
}
|
||||
|
||||
let target = target.assume_init();
|
||||
|
||||
let machine = LLVMCreateTargetMachine(
|
||||
target,
|
||||
target_triple.cast(),
|
||||
target_cpu.cast(),
|
||||
target_cpu_features.cast(),
|
||||
match session.optlevel {
|
||||
OptLevel::None => LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
|
||||
OptLevel::Less => LLVMCodeGenOptLevel::LLVMCodeGenLevelLess,
|
||||
OptLevel::Default => LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,
|
||||
OptLevel::Aggressive => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
|
||||
},
|
||||
if session.library {
|
||||
LLVMRelocMode::LLVMRelocDynamicNoPic
|
||||
} else {
|
||||
LLVMRelocMode::LLVMRelocDefault
|
||||
},
|
||||
LLVMCodeModel::LLVMCodeModelDefault,
|
||||
);
|
||||
|
||||
let filename = CString::new(target_path.as_os_str().to_string_lossy().as_bytes()).unwrap();
|
||||
tracing::debug!("filename to llvm: {:?}", filename);
|
||||
let ok = LLVMTargetMachineEmitToFile(
|
||||
machine,
|
||||
llvm_module,
|
||||
filename.as_ptr().cast_mut(),
|
||||
LLVMCodeGenFileType::LLVMObjectFile, // object (binary) or assembly (textual)
|
||||
error_buffer,
|
||||
);
|
||||
|
||||
if ok != 0 {
|
||||
let error = CStr::from_ptr(*error_buffer);
|
||||
let err = error.to_string_lossy().to_string();
|
||||
tracing::error!("error emitting to file: {:?}", err);
|
||||
LLVMDisposeMessage(*error_buffer);
|
||||
panic!("{err}")
|
||||
} else if !(*error_buffer).is_null() {
|
||||
LLVMDisposeMessage(*error_buffer);
|
||||
}
|
||||
|
||||
LLVMDisposeTargetMachine(machine);
|
||||
LLVMDisposeModule(llvm_module);
|
||||
LLVMContextDispose(llvm_context);
|
||||
|
||||
Ok(target_path)
|
||||
}
|
||||
}
|
||||
|
|
98
lib/edlang_codegen_mlir/src/linker.rs
Normal file
98
lib/edlang_codegen_mlir/src/linker.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::path::Path;
|
||||
|
||||
use tracing::instrument;
|
||||
|
||||
// TODO: Implement a proper linker driver, passing only the arguments needed dynamically based on the requirements.
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub fn link_shared_lib(input_path: &Path, output_filename: &Path) -> Result<(), std::io::Error> {
|
||||
let args: &[&str] = {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
&[
|
||||
"-demangle",
|
||||
"-no_deduplicate",
|
||||
"-dynamic",
|
||||
"-dylib",
|
||||
"-L/usr/local/lib",
|
||||
"-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib",
|
||||
&input_path.display().to_string(),
|
||||
"-o",
|
||||
&output_filename.display().to_string(),
|
||||
"-lSystem",
|
||||
]
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
&[
|
||||
"--hash-style=gnu",
|
||||
"--eh-frame-hdr",
|
||||
"-shared",
|
||||
"-o",
|
||||
&output_filename.display().to_string(),
|
||||
"-L/lib/../lib64",
|
||||
"-L/usr/lib/../lib64",
|
||||
"-lc",
|
||||
&input_path.display().to_string(),
|
||||
]
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
};
|
||||
|
||||
let mut linker = std::process::Command::new("ld");
|
||||
let proc = linker.args(args.iter()).spawn()?;
|
||||
proc.wait_with_output()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub fn link_binary(input_path: &Path, output_filename: &Path) -> Result<(), std::io::Error> {
|
||||
let args: &[&str] = {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
&[
|
||||
"-L/usr/local/lib",
|
||||
"-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib",
|
||||
&input_path.display().to_string(),
|
||||
"-o",
|
||||
&output_filename.display().to_string(),
|
||||
"-lSystem",
|
||||
]
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
&[
|
||||
"-pie",
|
||||
"--hash-style=gnu",
|
||||
"--eh-frame-hdr",
|
||||
"--dynamic-linker",
|
||||
"/lib64/ld-linux-x86-64.so.2",
|
||||
"-m",
|
||||
"elf_x86_64",
|
||||
"/usr/lib64/Scrt1.o",
|
||||
"/usr/lib64/crti.o",
|
||||
"-o",
|
||||
&output_filename.display().to_string(),
|
||||
"-L/lib64",
|
||||
"-L/usr/lib64",
|
||||
"-zrelro",
|
||||
"--no-as-needed",
|
||||
"-lc",
|
||||
"/usr/lib64/crtn.o",
|
||||
&input_path.display().to_string(),
|
||||
]
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
};
|
||||
|
||||
let mut linker = std::process::Command::new("ld");
|
||||
let proc = linker.args(args.iter()).spawn()?;
|
||||
proc.wait_with_output()?;
|
||||
Ok(())
|
||||
}
|
23
lib/edlang_codegen_mlir/src/wrappers.cpp
Normal file
23
lib/edlang_codegen_mlir/src/wrappers.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include <llvm-c/Support.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <mlir/CAPI/IR.h>
|
||||
#include <mlir/CAPI/Support.h>
|
||||
#include <mlir/CAPI/Wrap.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
|
||||
#include <mlir/IR/Types.h>
|
||||
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
|
||||
|
||||
extern "C" LLVMModuleRef mlirTranslateModuleToLLVMIR(MlirOperation module,
|
||||
LLVMContextRef context) {
|
||||
mlir::Operation *moduleOp = unwrap(module);
|
||||
|
||||
llvm::LLVMContext *ctx = llvm::unwrap(context);
|
||||
|
||||
std::unique_ptr<llvm::Module> llvmModule = mlir::translateModuleToLLVMIR(
|
||||
moduleOp, *ctx);
|
||||
|
||||
LLVMModuleRef moduleRef = llvm::wrap(llvmModule.release());
|
||||
|
||||
return moduleRef;
|
||||
}
|
|
@ -1,5 +1,87 @@
|
|||
use std::error::Error;
|
||||
use std::{error::Error, path::PathBuf, time::Instant};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct CompilerArgs {
|
||||
/// The input file.
|
||||
input: PathBuf,
|
||||
|
||||
/// Build for release with all optimizations.
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
release: bool,
|
||||
|
||||
/// Build as a library.
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
library: bool,
|
||||
}
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let args = CompilerArgs::parse();
|
||||
|
||||
/*
|
||||
let db = crate::db::Database::default();
|
||||
let source = ProgramSource::new(&db, std::fs::read_to_string(args.input.clone())?);
|
||||
tracing::debug!("source code:\n{}", source.input(&db));
|
||||
let program = match concrete_parser::parse_ast(&db, source) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
Diagnostics::dump(
|
||||
&db,
|
||||
source,
|
||||
&concrete_parser::parse_ast::accumulated::<concrete_parser::error::Diagnostics>(
|
||||
&db, source,
|
||||
),
|
||||
);
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
// todo: find a better name, "target" would clash with rust if running in the source tree.
|
||||
let target_dir = cwd.join("target_ed/");
|
||||
let output_file = target_dir.join(PathBuf::from(args.input.file_name().unwrap()));
|
||||
let output_file = if args.library {
|
||||
output_file.with_extension("so")
|
||||
} else {
|
||||
output_file.with_extension("")
|
||||
};
|
||||
|
||||
let session = Session {
|
||||
file_path: args.input,
|
||||
debug_info: if args.release {
|
||||
DebugInfo::None
|
||||
} else {
|
||||
DebugInfo::Full
|
||||
},
|
||||
optlevel: if args.release {
|
||||
OptLevel::Aggressive
|
||||
} else {
|
||||
OptLevel::None
|
||||
},
|
||||
source: source.input(&db).to_string(),
|
||||
library: args.library,
|
||||
target_dir,
|
||||
output_file,
|
||||
};
|
||||
tracing::debug!("Compiling with session: {:#?}", session);
|
||||
|
||||
// let object_path = concrete_codegen_mlir::compile(&session, &program)?;
|
||||
|
||||
if session.library {
|
||||
link_shared_lib(&object_path, &session.output_file.with_extension("so"))?;
|
||||
} else {
|
||||
link_binary(&object_path, &session.output_file.with_extension(""))?;
|
||||
}
|
||||
*/
|
||||
|
||||
let elapsed = start_time.elapsed();
|
||||
tracing::debug!("Done in {:?}", elapsed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ categories = ["compilers"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ariadne = { version = "0.4.0", features = ["auto-color"] }
|
||||
edlang_ast = { version = "0.1.0", path = "../edlang_ast" }
|
||||
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||
logos = "0.13.0"
|
||||
|
|
3
lib/edlang_parser/build.rs
Normal file
3
lib/edlang_parser/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
}
|
4
lib/edlang_parser/src/error.rs
Normal file
4
lib/edlang_parser/src/error.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
use crate::{lexer::LexicalError, tokens::Token};
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
pub type Error = ParseError<usize, Token, LexicalError>;
|
119
lib/edlang_parser/src/grammar.lalrpop
Normal file
119
lib/edlang_parser/src/grammar.lalrpop
Normal file
|
@ -0,0 +1,119 @@
|
|||
use crate::tokens::Token;
|
||||
use crate::lexer::LexicalError;
|
||||
use edlang_ast as ast;
|
||||
use std::str::FromStr;
|
||||
|
||||
grammar;
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexicalError;
|
||||
|
||||
enum Token {
|
||||
// keywords
|
||||
"let" => Token::KeywordLet,
|
||||
"const" => Token::KeywordConst,
|
||||
"fn" => Token::KeywordFn,
|
||||
"return" => Token::KeywordReturn,
|
||||
"struct" => Token::KeywordStruct,
|
||||
"if" => Token::KeywordIf,
|
||||
"else" => Token::KeywordElse,
|
||||
"while" => Token::KeywordWhile,
|
||||
"for" => Token::KeywordFor,
|
||||
"match" => Token::KeywordMatch,
|
||||
"mod" => Token::KeywordMod,
|
||||
"pub" => Token::KeywordPub,
|
||||
"mut" => Token::KeywordMut,
|
||||
"use" => Token::KeywordUse,
|
||||
|
||||
// literals
|
||||
"identifier" => Token::Identifier(<String>),
|
||||
"integer" => Token::Integer(<u64>),
|
||||
"string" => Token::String(<String>),
|
||||
"boolean" => Token::Boolean(<bool>),
|
||||
|
||||
// Other
|
||||
|
||||
"(" => Token::LeftParen,
|
||||
")" => Token::RightParen,
|
||||
"{" => Token::LeftBracket,
|
||||
"}" => Token::RightBracket,
|
||||
"[" => Token::LeftSquareBracket,
|
||||
"]" => Token::RightSquareBracket,
|
||||
"=" => Token::Assign,
|
||||
";" => Token::Semicolon,
|
||||
":" => Token::Colon,
|
||||
"->" => Token::Arrow,
|
||||
"," => Token::Coma,
|
||||
"<" => Token::LessThanSign,
|
||||
">" => Token::MoreThanSign,
|
||||
">=" => Token::MoreThanEqSign,
|
||||
"<=" => Token::LessThanEqSign,
|
||||
"." => Token::Dot,
|
||||
|
||||
// operators
|
||||
"+" => Token::OperatorAdd,
|
||||
"-" => Token::OperatorSub,
|
||||
"*" => Token::OperatorMul,
|
||||
"/" => Token::OperatorDiv,
|
||||
"%" => Token::OperatorRem,
|
||||
"&&" => Token::OperatorAnd,
|
||||
"||" => Token::OperatorOr,
|
||||
"==" => Token::OperatorEq,
|
||||
"!=" => Token::OperatorNe,
|
||||
"!" => Token::OperatorNot,
|
||||
"~" => Token::OperatorBitwiseNot,
|
||||
"^" => Token::OperatorBitwiseXor,
|
||||
"&" => Token::OperatorBitwiseAnd,
|
||||
"|" => Token::OperatorBitwiseOr,
|
||||
}
|
||||
}
|
||||
|
||||
// lalrpop macros
|
||||
|
||||
Dot<T>: Vec<T> = {
|
||||
<mut v:(<T> ".")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Comma<T>: Vec<T> = {
|
||||
<mut v:(<T> ",")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SemiColon<T>: Vec<T> = {
|
||||
<mut v:(<T> ";")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PlusSeparated<T>: Vec<T> = {
|
||||
<mut v:(<T> "+")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub(crate) Ident: ast::Ident = {
|
||||
<lo:@L> <name:"identifier"> <hi:@R> => ast::Ident {
|
||||
name,
|
||||
span: ast::Span::new(lo, hi),
|
||||
}
|
||||
}
|
47
lib/edlang_parser/src/lexer.rs
Normal file
47
lib/edlang_parser/src/lexer.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use std::{fmt::Display, ops::Range};
|
||||
|
||||
use logos::{Logos, SpannedIter};
|
||||
|
||||
use crate::tokens::{LexingError, Token};
|
||||
|
||||
pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LexicalError {
|
||||
InvalidToken(LexingError, Range<usize>),
|
||||
}
|
||||
|
||||
impl Display for LexicalError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LexicalError::InvalidToken(err, span) => {
|
||||
write!(f, "lexical error at ({:?}): {:?}", err, span)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lexer<'input> {
|
||||
// instead of an iterator over characters, we have a token iterator
|
||||
token_stream: SpannedIter<'input, Token>,
|
||||
}
|
||||
|
||||
impl<'input> Lexer<'input> {
|
||||
pub fn new(input: &'input str) -> Self {
|
||||
// the Token::lexer() method is provided by the Logos trait
|
||||
Self {
|
||||
token_stream: Token::lexer(input).spanned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> Iterator for Lexer<'input> {
|
||||
type Item = Spanned<Token, usize, LexicalError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.token_stream.next().map(|(token, span)| match token {
|
||||
Ok(token) => Ok((span.start, token, span.end)),
|
||||
Err(err) => Err(LexicalError::InvalidToken(err, span)),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1 +1,110 @@
|
|||
use std::{ops::Range, path::Path};
|
||||
|
||||
use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source};
|
||||
use error::Error;
|
||||
use lalrpop_util::ParseError;
|
||||
use lexer::{Lexer, LexicalError};
|
||||
|
||||
pub mod error;
|
||||
pub mod lexer;
|
||||
pub mod tokens;
|
||||
|
||||
pub mod grammar {
|
||||
#![allow(dead_code, unused_imports, unused_variables)]
|
||||
|
||||
pub use self::grammar::*;
|
||||
use lalrpop_util::lalrpop_mod;
|
||||
|
||||
lalrpop_mod!(pub grammar);
|
||||
}
|
||||
|
||||
pub fn parse_ast(source: &str) {
|
||||
let lexer = Lexer::new(source);
|
||||
let parser = grammar::IdentParser::new();
|
||||
}
|
||||
|
||||
pub fn print_error(path: &str, source: &str, error: &Error) -> Result<(), std::io::Error> {
|
||||
let source = Source::from(source);
|
||||
match error {
|
||||
ParseError::InvalidToken { location } => {
|
||||
let loc = *location;
|
||||
Report::build(ReportKind::Error, path, loc)
|
||||
.with_code(1)
|
||||
.with_message("Invalid token")
|
||||
.with_label(Label::new((path, loc..(loc + 1))).with_message("invalid token"))
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
ParseError::UnrecognizedEof { location, expected } => {
|
||||
let loc = *location;
|
||||
Report::build(ReportKind::Error, path, loc)
|
||||
.with_code(2)
|
||||
.with_message("Unrecognized end of file")
|
||||
.with_label(Label::new((path, loc..(loc + 1))).with_message(format!(
|
||||
"unrecognized eof, expected one of the following: {:?}",
|
||||
expected
|
||||
)))
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
ParseError::UnrecognizedToken { token, expected } => {
|
||||
Report::build(ReportKind::Error, path, token.0)
|
||||
.with_code(3)
|
||||
.with_message("Unrecognized token")
|
||||
.with_label(Label::new((path, token.0..token.2)).with_message(format!(
|
||||
"unrecognized token {:?}, expected one of the following: {:?}",
|
||||
token.1, expected
|
||||
)))
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
ParseError::ExtraToken { token } => {
|
||||
Report::build(ReportKind::Error, path, token.0)
|
||||
.with_code(4)
|
||||
.with_message("Extra token")
|
||||
.with_label(
|
||||
Label::new((path, token.0..token.2))
|
||||
.with_message(format!("unexpected extra token {:?}", token.1)),
|
||||
)
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
ParseError::User { error } => match error {
|
||||
LexicalError::InvalidToken(err, range) => match err {
|
||||
tokens::LexingError::NumberParseError => {
|
||||
Report::build(ReportKind::Error, path, range.start)
|
||||
.with_code(4)
|
||||
.with_message("Error parsing literal number")
|
||||
.with_label(
|
||||
Label::new((path, range.start..range.end))
|
||||
.with_message("error parsing literal number"),
|
||||
)
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
tokens::LexingError::Other => {
|
||||
Report::build(ReportKind::Error, path, range.start)
|
||||
.with_code(4)
|
||||
.with_message("Other error")
|
||||
.with_label(
|
||||
Label::new((path, range.start..range.end)).with_message("other error"),
|
||||
)
|
||||
.finish()
|
||||
.eprint((path, source))?;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::parse_ast;
|
||||
|
||||
#[test]
|
||||
fn test_ident() {
|
||||
parse_ast("hello");
|
||||
}
|
||||
}
|
||||
|
|
128
lib/edlang_parser/src/tokens.rs
Normal file
128
lib/edlang_parser/src/tokens.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
use logos::Logos;
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub enum LexingError {
|
||||
NumberParseError,
|
||||
#[default]
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<std::num::ParseIntError> for LexingError {
|
||||
fn from(_: std::num::ParseIntError) -> Self {
|
||||
LexingError::NumberParseError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for LexingError {
|
||||
fn from(_: Infallible) -> Self {
|
||||
LexingError::Other
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Logos, Debug, PartialEq, Clone)]
|
||||
#[logos(error = LexingError, skip r"[ \t\n\f]+", skip r"//[^\n]*", skip r"/\*(?:[^*]|\*[^/])*\*/")]
|
||||
pub enum Token {
|
||||
#[token("let")]
|
||||
KeywordLet,
|
||||
#[token("const")]
|
||||
KeywordConst,
|
||||
#[token("fn")]
|
||||
KeywordFn,
|
||||
#[token("return")]
|
||||
KeywordReturn,
|
||||
#[token("struct")]
|
||||
KeywordStruct,
|
||||
#[token("if")]
|
||||
KeywordIf,
|
||||
#[token("else")]
|
||||
KeywordElse,
|
||||
#[token("while")]
|
||||
KeywordWhile,
|
||||
#[token("for")]
|
||||
KeywordFor,
|
||||
#[token("match")]
|
||||
KeywordMatch,
|
||||
#[token("mod")]
|
||||
KeywordMod,
|
||||
#[token("pub")]
|
||||
KeywordPub,
|
||||
#[token("mut")]
|
||||
KeywordMut,
|
||||
#[token("use")]
|
||||
KeywordUse,
|
||||
|
||||
// Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/
|
||||
#[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())]
|
||||
Identifier(String),
|
||||
|
||||
// Literals
|
||||
#[regex(r"\d+", |lex| lex.slice().parse::<u64>().unwrap())]
|
||||
Integer(u64),
|
||||
#[regex(r#""(?:[^"]|\\")*""#, |lex| lex.slice().to_string())]
|
||||
String(String),
|
||||
#[regex(r"(true|false)", |lex| lex.slice().parse::<bool>().unwrap())]
|
||||
Boolean(bool),
|
||||
|
||||
#[token("(")]
|
||||
LeftParen,
|
||||
#[token(")")]
|
||||
RightParen,
|
||||
#[token("{")]
|
||||
LeftBracket,
|
||||
#[token("}")]
|
||||
RightBracket,
|
||||
#[token("[")]
|
||||
LeftSquareBracket,
|
||||
#[token("]")]
|
||||
RightSquareBracket,
|
||||
#[token("=")]
|
||||
Assign,
|
||||
#[token(";")]
|
||||
Semicolon,
|
||||
#[token(":")]
|
||||
Colon,
|
||||
#[token("->")]
|
||||
Arrow,
|
||||
#[token(",")]
|
||||
Coma,
|
||||
#[token(".")]
|
||||
Dot,
|
||||
#[token("<")]
|
||||
LessThanSign,
|
||||
#[token(">")]
|
||||
MoreThanSign,
|
||||
#[token(">=")]
|
||||
MoreThanEqSign,
|
||||
#[token("<=")]
|
||||
LessThanEqSign,
|
||||
|
||||
#[token("+")]
|
||||
OperatorAdd,
|
||||
#[token("-")]
|
||||
OperatorSub,
|
||||
#[token("*")]
|
||||
OperatorMul,
|
||||
#[token("/")]
|
||||
OperatorDiv,
|
||||
#[token("%")]
|
||||
OperatorRem,
|
||||
#[token("&&")]
|
||||
OperatorAnd,
|
||||
#[token("||")]
|
||||
OperatorOr,
|
||||
#[token("==")]
|
||||
OperatorEq,
|
||||
#[token("!=")]
|
||||
OperatorNe,
|
||||
#[token("!")]
|
||||
OperatorNot,
|
||||
#[token("~")]
|
||||
OperatorBitwiseNot,
|
||||
#[token("^")]
|
||||
OperatorBitwiseXor,
|
||||
#[token("&")]
|
||||
OperatorBitwiseAnd,
|
||||
#[token("|")]
|
||||
OperatorBitwiseOr,
|
||||
}
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ariadne = { version = "0.4.0", features = ["auto-color"] }
|
||||
|
|
|
@ -1 +1,29 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use ariadne::Source;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Session {
|
||||
pub file_path: PathBuf,
|
||||
pub debug_info: DebugInfo,
|
||||
pub optlevel: OptLevel,
|
||||
pub source: Source<String>,
|
||||
pub library: bool,
|
||||
/// The directory where to store artifacts and intermediate files such as object files.
|
||||
pub target_dir: PathBuf,
|
||||
pub output_file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
||||
pub enum OptLevel {
|
||||
None, // -O0
|
||||
Less, // -O1
|
||||
Default, // -O2
|
||||
Aggressive, // -O3
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
||||
pub enum DebugInfo {
|
||||
None,
|
||||
Full,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue