init
This commit is contained in:
commit
a42bb56a97
12 changed files with 991 additions and 0 deletions
2
.envrc
Normal file
2
.envrc
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
use flake
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
result
|
||||||
|
.direnv/
|
||||||
1
.nvimrc
Normal file
1
.nvimrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
lua vim.lsp.enable("rust_analyzer");
|
||||||
76
Cargo.lock
generated
Normal file
76
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "functlangv2"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"llvm-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.186"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "llvm-sys"
|
||||||
|
version = "211.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44007a7a44b73bdd877fa9c9ccef256036511220e90f65b4d50e7a15773c0ee3"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cc",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"regex-lite",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-lite"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "functlangv2"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
llvm-sys = "=211.0.1"
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782949081,
|
||||||
|
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782723713,
|
||||||
|
"narHash": "sha256-oPXCU/SSUokcGaJREHibG1CBX3+s/W7orDWQOZDsEeQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b5aa0fbd538984f6e3d201be0005b4463d8b09f8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782614948,
|
||||||
|
"narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
64
flake.nix
Normal file
64
flake.nix
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
description = "functional language written in rust";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
inputs:
|
||||||
|
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
imports = [
|
||||||
|
];
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
libs = with pkgs; [
|
||||||
|
llvm.lib
|
||||||
|
libxml2
|
||||||
|
zlib
|
||||||
|
];
|
||||||
|
utils = with pkgs; [
|
||||||
|
llvm.dev
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.functlang = pkgs.rustPlatform.buildRustPackage (finalAttrs: {
|
||||||
|
pname = "functlang";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
cargoHash = "sha256-39EegylerKYD03GD8GzdoATYIJxlyk9PKZnZwIR4SpI=";
|
||||||
|
|
||||||
|
buildInputs = libs;
|
||||||
|
nativeBuildInputs = utils;
|
||||||
|
});
|
||||||
|
packages.default = config.packages.functlang;
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
inputFrom = [ config.packages.functlang ];
|
||||||
|
buildInputs = libs;
|
||||||
|
nativeBuildInputs =
|
||||||
|
with pkgs;
|
||||||
|
[
|
||||||
|
rust-analyzer
|
||||||
|
rustfmt
|
||||||
|
]
|
||||||
|
++ utils;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
1
src/AST.rs
Normal file
1
src/AST.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
mod AST;
|
||||||
459
src/ast.rs
Normal file
459
src/ast.rs
Normal file
|
|
@ -0,0 +1,459 @@
|
||||||
|
use crate::{
|
||||||
|
codegen::{self, FloatOp},
|
||||||
|
lexer::Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ExprAST {
|
||||||
|
Number(f64),
|
||||||
|
Variable(String),
|
||||||
|
Binary(Box<ExprAST>, char, Box<ExprAST>),
|
||||||
|
Call(String, Vec<ExprAST>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PrototypeAST {
|
||||||
|
name: String,
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionAST {
|
||||||
|
proto: PrototypeAST,
|
||||||
|
body: ExprAST,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AST<R: std::io::Read> {
|
||||||
|
pub lexer: crate::lexer::Lexer<R>,
|
||||||
|
bin_op_precedence: std::collections::HashMap<char, i8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Lexer(crate::lexer::Error),
|
||||||
|
Ast(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExprAST {
|
||||||
|
pub fn codegen<'ctx>(
|
||||||
|
&self,
|
||||||
|
generator: &mut crate::codegen::Generator<'ctx, '_, '_>,
|
||||||
|
) -> Result<&'ctx mut llvm_sys::LLVMValue, crate::codegen::Error> {
|
||||||
|
match self {
|
||||||
|
ExprAST::Number(n) => generator.get_num(*n),
|
||||||
|
ExprAST::Variable(s) => match generator.named_values.get_mut(s) {
|
||||||
|
Some(v) => Ok(unsafe { (*v as *mut llvm_sys::LLVMValue).as_mut_unchecked() }),
|
||||||
|
None => Err(crate::codegen::Error::Generator("Unrecognised variable")),
|
||||||
|
},
|
||||||
|
ExprAST::Binary(lhs, op, rhs) => lhs.codegen(generator).and_then(|l| {
|
||||||
|
rhs.codegen(generator).and_then(|r| match op {
|
||||||
|
'<' => generator.bool_op(
|
||||||
|
l,
|
||||||
|
llvm_sys::LLVMRealPredicate::LLVMRealULT,
|
||||||
|
r,
|
||||||
|
c"cmptmp",
|
||||||
|
"Error generating < BinOp",
|
||||||
|
),
|
||||||
|
'>' => generator.bool_op(
|
||||||
|
l,
|
||||||
|
llvm_sys::LLVMRealPredicate::LLVMRealUGT,
|
||||||
|
r,
|
||||||
|
c"cmptmp",
|
||||||
|
"Error generating > BinOp",
|
||||||
|
),
|
||||||
|
'+' => generator.float_op(
|
||||||
|
l,
|
||||||
|
FloatOp::Add,
|
||||||
|
r,
|
||||||
|
c"addtmp",
|
||||||
|
"Error generating + BinOp",
|
||||||
|
),
|
||||||
|
'-' => generator.float_op(
|
||||||
|
l,
|
||||||
|
FloatOp::Sub,
|
||||||
|
r,
|
||||||
|
c"subtmp",
|
||||||
|
"Error generating - BinOp",
|
||||||
|
),
|
||||||
|
'*' => generator.float_op(
|
||||||
|
l,
|
||||||
|
FloatOp::Mul,
|
||||||
|
r,
|
||||||
|
c"multmp",
|
||||||
|
"Error generating * BinOp",
|
||||||
|
),
|
||||||
|
'/' => generator.float_op(
|
||||||
|
l,
|
||||||
|
FloatOp::Div,
|
||||||
|
r,
|
||||||
|
c"divtmp",
|
||||||
|
"Error generating / BinOp",
|
||||||
|
),
|
||||||
|
_ => Err(crate::codegen::Error::Generator("unknown OP")),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
ExprAST::Call(name, args) => match std::ffi::CString::new(name.as_str()) {
|
||||||
|
Ok(f) => generator.get_fun(&f).and_then(|callee| {
|
||||||
|
if (unsafe { llvm_sys::core::LLVMCountParams(callee) }
|
||||||
|
!= args.len().try_into().unwrap())
|
||||||
|
{
|
||||||
|
Err(crate::codegen::Error::Generator(
|
||||||
|
"incorrect # of argouments",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let mut argv: Vec<&mut llvm_sys::LLVMValue> = Vec::new();
|
||||||
|
let mut iter = args.iter();
|
||||||
|
while let Some(arg) = iter.next() {
|
||||||
|
match arg.codegen(generator) {
|
||||||
|
Ok(a) => argv.push(a),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMBuildCall2(
|
||||||
|
generator.builder,
|
||||||
|
llvm_sys::core::LLVMGlobalGetValueType(callee),
|
||||||
|
callee,
|
||||||
|
argv.as_mut_ptr().cast(),
|
||||||
|
args.len().try_into().unwrap(),
|
||||||
|
c"calltmp".as_ptr(),
|
||||||
|
)
|
||||||
|
.as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(crate::codegen::Error::Generator(
|
||||||
|
"Failed to generate function call",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Err(_) => Err(crate::codegen::Error::Generator(
|
||||||
|
"Error creating CString from function name",
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrototypeAST {
|
||||||
|
pub fn codegen<'ctx>(
|
||||||
|
&mut self,
|
||||||
|
generator: &mut crate::codegen::Generator<'ctx, '_, '_>,
|
||||||
|
) -> Result<&'ctx mut llvm_sys::LLVMValue, crate::codegen::Error> {
|
||||||
|
match unsafe { llvm_sys::core::LLVMDoubleTypeInContext(generator.context).as_mut() } {
|
||||||
|
Some(double) => {
|
||||||
|
let mut doubles: Vec<&'ctx mut llvm_sys::LLVMType> = Vec::new();
|
||||||
|
for _ in 0..self.args.len() {
|
||||||
|
doubles.push(unsafe { (double as *mut llvm_sys::LLVMType).as_mut_unchecked() });
|
||||||
|
}
|
||||||
|
|
||||||
|
match std::ffi::CString::new(self.name.as_str()) {
|
||||||
|
Ok(f) => generator
|
||||||
|
.make_fun(f.as_c_str(), double, &mut doubles)
|
||||||
|
.and_then(|fun| {
|
||||||
|
for i in 0..self.args.len() {
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMSetValueName2(
|
||||||
|
llvm_sys::core::LLVMGetParam(fun, i.try_into().unwrap()),
|
||||||
|
match std::ffi::CString::new(self.args[i].as_str()) {
|
||||||
|
Ok(s) => s.as_c_str().as_ptr(),
|
||||||
|
Err(_) => {
|
||||||
|
return Err(crate::codegen::Error::Generator(
|
||||||
|
"Failed to create CString for arg",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
self.args[i].len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fun)
|
||||||
|
}),
|
||||||
|
Err(_) => Err(crate::codegen::Error::Generator(
|
||||||
|
"failed to create CString from name",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(crate::codegen::Error::C("Failed to get double type")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionAST {
|
||||||
|
pub fn codegen<'ctx>(
|
||||||
|
&mut self,
|
||||||
|
generator: &mut crate::codegen::Generator<'ctx, '_, '_>,
|
||||||
|
) -> Result<&'ctx mut llvm_sys::LLVMValue, crate::codegen::Error> {
|
||||||
|
match std::ffi::CString::new(self.proto.name.as_str()) {
|
||||||
|
Ok(name) => generator
|
||||||
|
.get_fun(name.as_c_str())
|
||||||
|
.or_else(|_| self.proto.codegen(generator))
|
||||||
|
.and_then(|function| {
|
||||||
|
match unsafe { llvm_sys::core::LLVMGetFirstBasicBlock(function).is_null() } {
|
||||||
|
true => generator
|
||||||
|
.append_basic_block(c"entry", unsafe{ (function as *mut llvm_sys::LLVMValue).as_mut_unchecked() })
|
||||||
|
.and_then(|bb| {
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMPositionBuilderAtEnd(generator.builder, bb);
|
||||||
|
};
|
||||||
|
generator.named_values.clear();
|
||||||
|
for i in 0..self.proto.args.len() {
|
||||||
|
let mut len: usize = 0;
|
||||||
|
unsafe {
|
||||||
|
match llvm_sys::core::LLVMGetParam(
|
||||||
|
function,
|
||||||
|
i.try_into().unwrap(),
|
||||||
|
)
|
||||||
|
.as_mut()
|
||||||
|
{
|
||||||
|
None => {
|
||||||
|
return Err(codegen::Error::C(
|
||||||
|
"Failed to construct arg",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(arg) => match (llvm_sys::core::LLVMGetValueName2(
|
||||||
|
arg, &mut len,
|
||||||
|
)
|
||||||
|
as *const u8)
|
||||||
|
.as_ref()
|
||||||
|
{
|
||||||
|
None => {
|
||||||
|
return Err(codegen::Error::Generator(
|
||||||
|
"failed to get arg name",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some(arg_name) => {
|
||||||
|
generator.named_values.insert(
|
||||||
|
String::from(
|
||||||
|
str::from_utf8(
|
||||||
|
std::slice::from_raw_parts(
|
||||||
|
arg_name, len,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
arg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.body.codegen(generator) {
|
||||||
|
Ok(ret_val) => if unsafe {llvm_sys::core::LLVMBuildRet(generator.builder, ret_val).is_null()} {
|
||||||
|
unsafe{ llvm_sys::core::LLVMDeleteFunction(function); }
|
||||||
|
Err(codegen::Error::Generator("failed to generate RetVal"))
|
||||||
|
} else {
|
||||||
|
unsafe{llvm_sys::analysis::LLVMVerifyFunction(function, llvm_sys::analysis::LLVMVerifierFailureAction::LLVMPrintMessageAction)};
|
||||||
|
Ok(function)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
unsafe{ llvm_sys::core::LLVMDeleteFunction(function); }
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
false => Err(crate::codegen::Error::Generator(
|
||||||
|
"function cannot be redefined",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Err(_) => Err(crate::codegen::Error::Generator(
|
||||||
|
"Failed to create CString for function name",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read> AST<R> {
|
||||||
|
pub fn new(
|
||||||
|
lexer: crate::lexer::Lexer<R>,
|
||||||
|
bin_op_precedence: std::collections::HashMap<char, i8>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
lexer,
|
||||||
|
bin_op_precedence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parenthesis should not be put back in
|
||||||
|
fn parse_paren_expr(&mut self) -> Result<ExprAST, Error> {
|
||||||
|
self.parse_expression()
|
||||||
|
.and_then(|v| match self.lexer.next() {
|
||||||
|
Ok(Token::Other(')')) => Ok(v),
|
||||||
|
Ok(_) => Err(Error::Ast("expected ')'")),
|
||||||
|
Err(e) => Err(Error::Lexer(e)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// id should be put back in
|
||||||
|
fn parse_identifier_expr(&mut self) -> Result<ExprAST, Error> {
|
||||||
|
let id = match self.lexer.next() {
|
||||||
|
Ok(Token::Identifier(i)) => i,
|
||||||
|
Ok(_) => return Err(Error::Ast("internal ast error: lexer missing identfier")),
|
||||||
|
Err(e) => return Err(Error::Lexer(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.lexer.next() {
|
||||||
|
Ok(Token::Other('(')) => {}
|
||||||
|
Ok(t) => {
|
||||||
|
self.lexer.store(t);
|
||||||
|
return Ok(ExprAST::Variable(id));
|
||||||
|
}
|
||||||
|
Err(e) => return Err(Error::Lexer(e)),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args: Vec<ExprAST> = Vec::new();
|
||||||
|
|
||||||
|
if match self.lexer.next() {
|
||||||
|
Ok(Token::Other(')')) => false,
|
||||||
|
Ok(t) => {
|
||||||
|
self.lexer.store(t);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => return Err(Error::Lexer(e)),
|
||||||
|
} {
|
||||||
|
loop {
|
||||||
|
match self.parse_expression() {
|
||||||
|
Ok(t) => args.push(t),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
if match self.lexer.next() {
|
||||||
|
Ok(Token::Other(')')) => true,
|
||||||
|
Ok(Token::Other(',')) => false,
|
||||||
|
Ok(_) => return Err(Error::Ast("expected ')' or ',' in argoument list")),
|
||||||
|
Err(e) => return Err(Error::Lexer(e)),
|
||||||
|
} {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ExprAST::Call(id, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_primary(&mut self) -> Result<ExprAST, Error> {
|
||||||
|
match self.lexer.next() {
|
||||||
|
Ok(Token::Identifier(s)) => {
|
||||||
|
self.lexer.store(Token::Identifier(s));
|
||||||
|
self.parse_identifier_expr()
|
||||||
|
}
|
||||||
|
Ok(Token::Number(n)) => Ok(ExprAST::Number(n)),
|
||||||
|
|
||||||
|
Ok(Token::Other('(')) => self.parse_paren_expr(),
|
||||||
|
|
||||||
|
Ok(_) => Err(Error::Ast("expected identifier or number or '('")),
|
||||||
|
|
||||||
|
Err(e) => Err(Error::Lexer(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// token should be in the lexer
|
||||||
|
fn get_tok_precedence(&mut self) -> Result<(i8, char), Error> {
|
||||||
|
match self.lexer.next() {
|
||||||
|
Ok(Token::Other(c)) => match self.bin_op_precedence.get(&c) {
|
||||||
|
Some(p) => Ok((*p, c)),
|
||||||
|
None => {
|
||||||
|
self.lexer.store(Token::Other(c));
|
||||||
|
Err(Error::Ast("passed unknown bin_op"))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(t) => {
|
||||||
|
self.lexer.store(t);
|
||||||
|
Err(Error::Ast(
|
||||||
|
"token passed to get_tok_precedence is not a char",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(e) => Err(Error::Lexer(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expression(&mut self) -> Result<ExprAST, Error> {
|
||||||
|
self.parse_primary()
|
||||||
|
.and_then(|lhs| self.parse_bin_op_rhs(0, lhs))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bin_op_rhs(&mut self, exec_prec: i8, lhs: ExprAST) -> Result<ExprAST, Error> {
|
||||||
|
match self.get_tok_precedence() {
|
||||||
|
Ok((tok_prec, op)) => {
|
||||||
|
if tok_prec < exec_prec {
|
||||||
|
Ok(lhs)
|
||||||
|
} else {
|
||||||
|
self.parse_primary()
|
||||||
|
.and_then(|rhs| match self.get_tok_precedence() {
|
||||||
|
Ok((new_prec, new_op)) => {
|
||||||
|
if tok_prec < new_prec {
|
||||||
|
self.lexer.store(Token::Other(new_op));
|
||||||
|
self.parse_bin_op_rhs(tok_prec + 1, rhs)
|
||||||
|
.and_then(|new_rhs| {
|
||||||
|
self.parse_bin_op_rhs(
|
||||||
|
exec_prec,
|
||||||
|
ExprAST::Binary(
|
||||||
|
Box::new(lhs),
|
||||||
|
op,
|
||||||
|
Box::new(new_rhs),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.parse_bin_op_rhs(
|
||||||
|
exec_prec,
|
||||||
|
ExprAST::Binary(Box::new(lhs), op, Box::new(rhs)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::Ast(_)) => self.parse_bin_op_rhs(
|
||||||
|
exec_prec,
|
||||||
|
ExprAST::Binary(Box::new(lhs), op, Box::new(rhs)),
|
||||||
|
),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::Ast(_)) => Ok(lhs),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// id should be in the lexer
|
||||||
|
pub fn parse_prototype(&mut self) -> Result<PrototypeAST, Error> {
|
||||||
|
match self.lexer.next() {
|
||||||
|
Ok(Token::Identifier(name)) => match self.lexer.next() {
|
||||||
|
Ok(Token::Other('(')) => {
|
||||||
|
let mut args: Vec<String> = Vec::new();
|
||||||
|
loop {
|
||||||
|
match self.lexer.next() {
|
||||||
|
Ok(Token::Identifier(id)) => args.push(id),
|
||||||
|
Ok(Token::Other(')')) => break Ok(PrototypeAST { name, args }),
|
||||||
|
Ok(_) => return Err(Error::Ast("missing ')' in prototype")),
|
||||||
|
Err(e) => return Err(Error::Lexer(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_) => Err(Error::Ast("expected '(' in prototype")),
|
||||||
|
Err(e) => Err(Error::Lexer(e)),
|
||||||
|
},
|
||||||
|
Ok(_) => Err(Error::Ast("expected an identifier")),
|
||||||
|
Err(e) => Err(Error::Lexer(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def need not be in the lexer
|
||||||
|
pub fn parse_definition(&mut self) -> Result<FunctionAST, Error> {
|
||||||
|
self.parse_prototype().and_then(|proto| {
|
||||||
|
self.parse_expression()
|
||||||
|
.map(|body| FunctionAST { proto, body })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_top_level_expr(&mut self) -> Result<FunctionAST, Error> {
|
||||||
|
self.parse_expression().map(|body| FunctionAST {
|
||||||
|
proto: PrototypeAST {
|
||||||
|
name: "__anon_expr".to_string(),
|
||||||
|
args: Vec::new(),
|
||||||
|
},
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/codegen.rs
Normal file
125
src/codegen.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
pub struct Generator<'ctx, 'module, 'builder> {
|
||||||
|
pub context: &'ctx mut llvm_sys::LLVMContext,
|
||||||
|
pub module: &'module mut llvm_sys::LLVMModule,
|
||||||
|
pub builder: &'builder mut llvm_sys::LLVMBuilder,
|
||||||
|
pub named_values: std::collections::HashMap<String, &'ctx mut llvm_sys::LLVMValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FloatOp {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'module, 'builder> Generator<'ctx, 'module, 'builder> {
|
||||||
|
pub fn new(
|
||||||
|
context: &'ctx mut llvm_sys::LLVMContext,
|
||||||
|
module: &'module mut llvm_sys::LLVMModule,
|
||||||
|
builder: &'builder mut llvm_sys::LLVMBuilder,
|
||||||
|
named_values: std::collections::HashMap<String, &'ctx mut llvm_sys::LLVMValue>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
context,
|
||||||
|
module,
|
||||||
|
builder,
|
||||||
|
named_values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_num<'a>(&mut self, num: f64) -> Result<&'a mut llvm_sys::LLVMValue, Error> {
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMConstReal(
|
||||||
|
llvm_sys::core::LLVMDoubleTypeInContext(self.context),
|
||||||
|
num,
|
||||||
|
)
|
||||||
|
.as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(Error::C("Error generating floating point number"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bool_op<'a>(
|
||||||
|
&mut self,
|
||||||
|
lhs: &mut llvm_sys::LLVMValue,
|
||||||
|
op: llvm_sys::LLVMRealPredicate,
|
||||||
|
rhs: &mut llvm_sys::LLVMValue,
|
||||||
|
name: &std::ffi::CStr,
|
||||||
|
err: &'static str,
|
||||||
|
) -> Result<&'a mut llvm_sys::LLVMValue, Error> {
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMBuildUIToFP(
|
||||||
|
self.builder,
|
||||||
|
llvm_sys::core::LLVMBuildFCmp(self.builder, op, lhs, rhs, name.as_ptr()),
|
||||||
|
llvm_sys::core::LLVMDoubleTypeInContext(self.context),
|
||||||
|
c"booltmp".as_ptr(),
|
||||||
|
)
|
||||||
|
.as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(Error::C(err))
|
||||||
|
}
|
||||||
|
pub fn float_op<'a>(
|
||||||
|
&mut self,
|
||||||
|
lhs: &mut llvm_sys::LLVMValue,
|
||||||
|
op: FloatOp,
|
||||||
|
rhs: &mut llvm_sys::LLVMValue,
|
||||||
|
name: &std::ffi::CStr,
|
||||||
|
err: &'static str,
|
||||||
|
) -> Result<&'a mut llvm_sys::LLVMValue, Error> {
|
||||||
|
unsafe {
|
||||||
|
let fun = match op {
|
||||||
|
FloatOp::Add => llvm_sys::core::LLVMBuildFAdd,
|
||||||
|
FloatOp::Sub => llvm_sys::core::LLVMBuildFSub,
|
||||||
|
FloatOp::Mul => llvm_sys::core::LLVMBuildFMul,
|
||||||
|
FloatOp::Div => llvm_sys::core::LLVMBuildFDiv,
|
||||||
|
};
|
||||||
|
fun(self.builder, lhs, rhs, name.as_ptr()).as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(Error::C(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_fun(
|
||||||
|
&mut self,
|
||||||
|
name: &std::ffi::CStr,
|
||||||
|
ret_type: &mut llvm_sys::LLVMType,
|
||||||
|
args: &mut Vec<&'ctx mut llvm_sys::LLVMType>,
|
||||||
|
) -> Result<&'ctx mut llvm_sys::LLVMValue, Error> {
|
||||||
|
match args.len().try_into() {
|
||||||
|
Ok(len) => unsafe {
|
||||||
|
llvm_sys::core::LLVMAddFunction(
|
||||||
|
self.module,
|
||||||
|
name.as_ptr(),
|
||||||
|
llvm_sys::core::LLVMFunctionType(ret_type, args.as_mut_ptr().cast(), len, 0),
|
||||||
|
)
|
||||||
|
.as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(Error::Generator("failed to create function")),
|
||||||
|
Err(_) => Err(Error::Generator("too many args")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fun<'a>(
|
||||||
|
&mut self,
|
||||||
|
name: &std::ffi::CStr,
|
||||||
|
) -> Result<&'a mut llvm_sys::LLVMValue, Error> {
|
||||||
|
unsafe { llvm_sys::core::LLVMGetNamedFunction(self.module, name.as_ptr()).as_mut() }
|
||||||
|
.ok_or(Error::Generator("unkown function name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_basic_block(
|
||||||
|
&mut self,
|
||||||
|
name: &std::ffi::CStr,
|
||||||
|
function: &'ctx mut llvm_sys::LLVMValue,
|
||||||
|
) -> Result<&'ctx mut llvm_sys::LLVMBasicBlock, Error> {
|
||||||
|
unsafe {
|
||||||
|
llvm_sys::core::LLVMAppendBasicBlockInContext(self.context, function, name.as_ptr())
|
||||||
|
.as_mut()
|
||||||
|
}
|
||||||
|
.ok_or(Error::Generator("failed to create basic block"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
C(&'static str),
|
||||||
|
Generator(&'static str),
|
||||||
|
}
|
||||||
144
src/lexer.rs
Normal file
144
src/lexer.rs
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Token {
|
||||||
|
Eof,
|
||||||
|
Def,
|
||||||
|
Extern,
|
||||||
|
Identifier(String),
|
||||||
|
Number(f64),
|
||||||
|
Other(char),
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl std::cmp::PartialEq for Token {
|
||||||
|
// fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
// match (self, rhs) {
|
||||||
|
// (Token::Eof, Token::Eof)
|
||||||
|
// | (Token::Def, Token::Def)
|
||||||
|
// | (Token::Extern, Token::Extern) => true,
|
||||||
|
// (Token::Identifier(a), Token::Identifier(b)) => a == b,
|
||||||
|
// (Token::Number(a), Token::Number(b)) => a == b,
|
||||||
|
// (Token::Other(a), Token::Other(b)) => a == b,
|
||||||
|
// _ => false,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Io(std::io::Error),
|
||||||
|
Float(std::num::ParseFloatError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Io(err) => err.fmt(f),
|
||||||
|
Error::Float(err) => err.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Lexer<R: std::io::Read> {
|
||||||
|
reader: std::iter::Peekable<std::io::Bytes<R>>,
|
||||||
|
buf: Option<Token>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read> Lexer<R> {
|
||||||
|
pub fn new(reader: std::iter::Peekable<std::io::Bytes<R>>) -> Lexer<R> {
|
||||||
|
return Lexer {
|
||||||
|
reader,
|
||||||
|
buf: Option::None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(&mut self, tok: Token) -> Option<Token> {
|
||||||
|
self.buf.replace(tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Result<Token, Error> {
|
||||||
|
match self.buf.take() {
|
||||||
|
Some(t) => {
|
||||||
|
return Ok(t);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
while match self.reader.peek() {
|
||||||
|
Some(Ok(c)) => c.is_ascii_whitespace(),
|
||||||
|
None => return Ok(Token::Eof),
|
||||||
|
Some(Err(_)) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
self.reader.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if match self.reader.peek().unwrap() {
|
||||||
|
Ok(c) => c.is_ascii_alphabetic(),
|
||||||
|
Err(_) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
let mut id = String::new();
|
||||||
|
while match self.reader.peek() {
|
||||||
|
Some(Ok(c)) => c.is_ascii_alphanumeric(),
|
||||||
|
None => false,
|
||||||
|
Some(Err(_)) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
id.push(self.reader.next().unwrap().unwrap() as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(match id.as_str() {
|
||||||
|
"def" => Token::Def,
|
||||||
|
"extern" => Token::Extern,
|
||||||
|
_ => Token::Identifier(id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if match self.reader.peek().unwrap() {
|
||||||
|
Ok(c) => c.is_ascii_digit() || *c == '.' as u8,
|
||||||
|
Err(_) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
let mut num = String::new();
|
||||||
|
while match self.reader.peek() {
|
||||||
|
Some(Ok(c)) => c.is_ascii_digit() || *c == '.' as u8,
|
||||||
|
None => false,
|
||||||
|
Some(Err(_)) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
num.push(self.reader.next().unwrap().unwrap() as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match num.parse::<f64>() {
|
||||||
|
Ok(n) => Ok(Token::Number(n)),
|
||||||
|
Err(e) => Err(Error::Float(e)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if match self.reader.peek().unwrap() {
|
||||||
|
Ok(c) => *c == '#' as u8,
|
||||||
|
Err(_) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
while match self.reader.peek() {
|
||||||
|
Some(Ok(c)) => *c != '\n' as u8 && *c != '\r' as u8,
|
||||||
|
None => return Ok(Token::Eof),
|
||||||
|
Some(Err(_)) => return Err(Error::Io(self.reader.next().unwrap().unwrap_err())),
|
||||||
|
} {
|
||||||
|
self.reader.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Token::Other(self.reader.next().unwrap().unwrap() as char))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
pub fn is(&self, rhs: Self) -> bool {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Token::Eof, Token::Eof)
|
||||||
|
| (Token::Def, Token::Def)
|
||||||
|
| (Token::Extern, Token::Extern)
|
||||||
|
| (Token::Identifier(_), Token::Identifier(_))
|
||||||
|
| (Token::Number(_), Token::Number(_))
|
||||||
|
| (Token::Other(_), Token::Other(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main.rs
Normal file
48
src/main.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
mod ast;
|
||||||
|
mod lexer;
|
||||||
|
mod codegen;
|
||||||
|
|
||||||
|
use lexer::Token;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
const EXAMPLE_PROGAM: &'static [u8] = b"
|
||||||
|
# Compute the x'th fibonacci number.
|
||||||
|
def fib(x)
|
||||||
|
if x < 3 then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
fib(x-1)+fib(x-2)
|
||||||
|
|
||||||
|
# This expression will compute the 40th number.
|
||||||
|
fib(40)
|
||||||
|
";
|
||||||
|
|
||||||
|
//const EXAMPLE_PROGAM: &'static [u8] = b"extern sin(a)";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut ast = ast::AST::new(
|
||||||
|
lexer::Lexer::new(EXAMPLE_PROGAM.bytes().peekable()),
|
||||||
|
std::collections::HashMap::from([
|
||||||
|
('<', 10),
|
||||||
|
('>', 10),
|
||||||
|
('+', 20),
|
||||||
|
('-', 20),
|
||||||
|
('*', 40),
|
||||||
|
('/', 40),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match ast.lexer.next() {
|
||||||
|
Ok(Token::Def) => println!("{:?}", ast.parse_definition()),
|
||||||
|
Ok(Token::Extern) => println!("{:?}", ast.parse_prototype()),
|
||||||
|
Ok(Token::Other(';')) => continue,
|
||||||
|
Ok(Token::Eof) => break,
|
||||||
|
Ok(t) => {
|
||||||
|
ast.lexer.store(t);
|
||||||
|
println!("{:?}", ast.parse_top_level_expr());
|
||||||
|
}
|
||||||
|
Err(e) => println!("{e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue