This commit is contained in:
Dario48 2026-07-12 13:14:28 +02:00
commit a42bb56a97
12 changed files with 991 additions and 0 deletions

2
.envrc Normal file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
use flake

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
result
.direnv/

1
.nvimrc Normal file
View file

@ -0,0 +1 @@
lua vim.lsp.enable("rust_analyzer");

76
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
mod AST;

459
src/ast.rs Normal file
View 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
View 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
View 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
View 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}"),
}
}
}