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