START OF COMPILER
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/target
|
||||
/build
|
||||
|
||||
@@ -20,3 +20,12 @@ fn main() {
|
||||
b
|
||||
};
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let r = 3;
|
||||
let a = }
|
||||
}
|
||||
|
||||
fn test2() {
|
||||
|
||||
}
|
||||
4
data/test.lang
Normal file
4
data/test.lang
Normal file
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
let x = 3;
|
||||
print(x);
|
||||
}
|
||||
42
src/compiler/mod.rs
Normal file
42
src/compiler/mod.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::{
|
||||
fs::{create_dir_all, OpenOptions},
|
||||
os::unix::fs::OpenOptionsExt,
|
||||
path::Path,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
mod riscv64;
|
||||
mod program;
|
||||
mod target;
|
||||
|
||||
pub fn main() {
|
||||
use std::io::prelude::*;
|
||||
let dir = Path::new("build");
|
||||
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
||||
let name = Path::new("test");
|
||||
let path = dir.join(name);
|
||||
let path = path.as_os_str();
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.mode(0o750)
|
||||
.open(path)
|
||||
.expect("Failed to create file");
|
||||
file.write_all(&riscv64::gen())
|
||||
.expect("Failed to write to file");
|
||||
file.sync_all().expect("Failed to sync file");
|
||||
if let Ok(mut process) = Command::new("qemu-riscv64").arg(path).spawn() {
|
||||
if let Ok(status) = process.wait() {
|
||||
if status.code().is_none_or(|c| c != 0) {
|
||||
println!("{}", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// qemu-riscv64 -g 1234 test &
|
||||
// riscv64-linux-gnu-gdb -q \
|
||||
// -ex "target remote :1234" \
|
||||
// test
|
||||
|
||||
53
src/compiler/program.rs
Normal file
53
src/compiler/program.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Program {
|
||||
data: Vec<u8>,
|
||||
ro_map: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(data: HashMap<String, Vec<u8>>) -> Self {
|
||||
let mut ro_data = Vec::new();
|
||||
let mut ro_map = HashMap::new();
|
||||
for (key, val) in data {
|
||||
ro_map.insert(key, ro_data.len());
|
||||
ro_data.extend(val);
|
||||
}
|
||||
Self {
|
||||
data: ro_data,
|
||||
ro_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_program<I: Instr>(
|
||||
ro_data: HashMap<String, Vec<u8>>,
|
||||
functions: Vec<Function<I>>,
|
||||
) -> Vec<u8> {
|
||||
let mut data = Vec::new();
|
||||
let mut ro_map = HashMap::new();
|
||||
for (key, val) in ro_data {
|
||||
ro_map.insert(key, data.len());
|
||||
data.extend(val);
|
||||
}
|
||||
// let mut fn_map = HashMap::new();
|
||||
for fun in functions {
|
||||
for i in fun.instructions {
|
||||
data.extend(i.to_le_bytes());
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
pub struct Function<I: Instr> {
|
||||
label: String,
|
||||
instructions: Vec<I>,
|
||||
}
|
||||
|
||||
pub trait Instr {
|
||||
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8>;
|
||||
}
|
||||
|
||||
struct SymbolInstr {
|
||||
i: usize
|
||||
}
|
||||
2
src/compiler/riscv64/asm.rs
Normal file
2
src/compiler/riscv64/asm.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub enum Instruction {
|
||||
}
|
||||
101
src/compiler/riscv64/elf.rs
Normal file
101
src/compiler/riscv64/elf.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
#[repr(C)]
|
||||
pub struct ELF64Header {
|
||||
magic: u32,
|
||||
class: u8,
|
||||
endianness: u8,
|
||||
ei_version: u8,
|
||||
os_abi: u8,
|
||||
os_abi_ver: u8,
|
||||
pad: [u8; 7],
|
||||
ty: u16,
|
||||
machine: u16,
|
||||
e_version: u32,
|
||||
entry: u64,
|
||||
program_header_offset: u64,
|
||||
section_header_offset: u64,
|
||||
flags: u32,
|
||||
header_size: u16,
|
||||
program_header_entry_size: u16,
|
||||
program_header_num: u16,
|
||||
section_header_entry_size: u16,
|
||||
section_header_num: u16,
|
||||
section_header_str_idx: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ProgramHeader {
|
||||
ty: u32,
|
||||
flags: u32,
|
||||
offset: u64,
|
||||
vaddr: u64,
|
||||
paddr: u64,
|
||||
filesz: u64,
|
||||
memsz: u64,
|
||||
align: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SectionHeader {
|
||||
name_idx: u32,
|
||||
ty: u32,
|
||||
flags: u64,
|
||||
addr: u64,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
link: u32,
|
||||
info: u32,
|
||||
addr_align: u64,
|
||||
entry_size: u64,
|
||||
}
|
||||
|
||||
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
|
||||
let addr_start = 0x1000;
|
||||
let page_size = 0x1000;
|
||||
let progam_size = std::mem::size_of_val(&program[..]) as u64;
|
||||
let program_header = ProgramHeader {
|
||||
ty: 0x1, // LOAD
|
||||
flags: 0b101, // executable, readable
|
||||
offset: 0x0,
|
||||
vaddr: addr_start,
|
||||
paddr: addr_start,
|
||||
filesz: progam_size,
|
||||
memsz: progam_size,
|
||||
align: page_size,
|
||||
};
|
||||
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
|
||||
let program_pos = header_len;
|
||||
let header = ELF64Header {
|
||||
magic: 0x7f_45_4c_46u32.swap_bytes(),
|
||||
class: 0x2, // 64 bit
|
||||
endianness: 0x1, // little endian
|
||||
ei_version: 0x1,
|
||||
os_abi: 0x0, // system-v
|
||||
os_abi_ver: 0x0,
|
||||
pad: [0x0; 7],
|
||||
ty: 0x2, // executable
|
||||
machine: 0xf3, // risc-v
|
||||
e_version: 0x1,
|
||||
entry: addr_start + program_pos + start_offset,
|
||||
program_header_offset: size_of::<ELF64Header>() as u64,
|
||||
section_header_offset: 0x0,
|
||||
// C ABI (16 bit instruction align) + double precision floats
|
||||
flags: 0x1 | 0x4,
|
||||
header_size: size_of::<ELF64Header>() as u16,
|
||||
program_header_entry_size: size_of::<ProgramHeader>() as u16,
|
||||
program_header_num: 0x1,
|
||||
section_header_entry_size: size_of::<SectionHeader>() as u16,
|
||||
section_header_num: 0x0,
|
||||
section_header_str_idx: 0x0,
|
||||
};
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
bytes.extend(as_u8_slice(&header));
|
||||
bytes.extend(as_u8_slice(&program_header));
|
||||
bytes.extend(program);
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
|
||||
core::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
|
||||
}
|
||||
58
src/compiler/riscv64/instruction/base.rs
Normal file
58
src/compiler/riscv64/instruction/base.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use super::{Reg, OPCODE_MASK};
|
||||
use crate::{compiler::program::Instr, util::{bit, bits, in_bit_range}};
|
||||
|
||||
pub struct Instruction(u32);
|
||||
|
||||
use Instruction as I;
|
||||
|
||||
impl Instr for Instruction {
|
||||
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_le_bytes().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn r_type(funct7: u32, rs2: Reg, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
|
||||
I((funct7 << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn i_type(imm: i32, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 11, 0));
|
||||
I((bits(imm, 11, 0) << 20) + (rs1.val() << 15) + (funct3 << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 11, 0));
|
||||
I((bits(imm, 11, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 12)
|
||||
+ (bits(imm, 4, 0) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 12, 1));
|
||||
I((bit(imm, 12) << 31)
|
||||
+ (bits(imm, 10, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 8)
|
||||
+ (bits(imm, 4, 1) << 8)
|
||||
+ (bit(imm, 11) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn u_type(imm: i32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 31, 12));
|
||||
I((bits(imm, 31, 12) << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn j_type(imm: i32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 20, 1));
|
||||
I((bit(imm, 20) << 31)
|
||||
+ (bits(imm, 10, 1) << 21)
|
||||
+ (bit(imm, 11) << 20)
|
||||
+ (bits(imm, 19, 12) << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
20
src/compiler/riscv64/instruction/func.rs
Normal file
20
src/compiler/riscv64/instruction/func.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
pub mod op {
|
||||
pub const ADD : u32 = 0b000;
|
||||
pub const SLL : u32 = 0b001;
|
||||
pub const SLT : u32 = 0b010;
|
||||
pub const SLTU: u32 = 0b011;
|
||||
pub const XOR : u32 = 0b100;
|
||||
pub const SR : u32 = 0b101;
|
||||
pub const OR : u32 = 0b110;
|
||||
pub const AND : u32 = 0b111;
|
||||
}
|
||||
|
||||
pub mod width {
|
||||
pub const B : u32 = 0b000;
|
||||
pub const H : u32 = 0b001;
|
||||
pub const W : u32 = 0b010;
|
||||
pub const D : u32 = 0b011;
|
||||
pub const BU: u32 = 0b100;
|
||||
pub const HU: u32 = 0b101;
|
||||
pub const WU: u32 = 0b110;
|
||||
}
|
||||
53
src/compiler/riscv64/instruction/mod.rs
Normal file
53
src/compiler/riscv64/instruction/mod.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
mod base;
|
||||
mod reg;
|
||||
mod opcode;
|
||||
mod func;
|
||||
|
||||
use base::*;
|
||||
pub use reg::*;
|
||||
use Instruction as I;
|
||||
use opcode::*;
|
||||
use func::{op::*, width};
|
||||
|
||||
pub const fn ecall() -> I {
|
||||
i_type(0, zero, 0, zero, SYSTEM)
|
||||
}
|
||||
pub const fn ebreak() -> I {
|
||||
i_type(1, zero, 0, zero, SYSTEM)
|
||||
}
|
||||
pub const fn auipc(dest: Reg, imm: i32) -> I {
|
||||
u_type(imm, dest, AUIPC)
|
||||
}
|
||||
pub const fn ld(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::D, dest, LOAD)
|
||||
}
|
||||
pub const fn lw(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::W, dest, LOAD)
|
||||
}
|
||||
pub const fn lb(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::B, dest, LOAD)
|
||||
}
|
||||
pub const fn sb(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::B, offset, STORE)
|
||||
}
|
||||
pub const fn sw(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::W, offset, STORE)
|
||||
}
|
||||
pub const fn sd(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::D, offset, STORE)
|
||||
}
|
||||
pub const fn addi(dest: Reg, src: Reg, imm: i32) -> I {
|
||||
i_type(imm, src, ADD, dest, IMM_OP)
|
||||
}
|
||||
pub const fn jal(offset: i32, dest: Reg) -> I {
|
||||
j_type(offset, dest, JAL)
|
||||
}
|
||||
|
||||
// pseudo instructions that map to a single instruction
|
||||
|
||||
pub const fn j(offset: i32) -> I {
|
||||
jal(offset, zero)
|
||||
}
|
||||
pub const fn mv(dest: Reg, src: Reg) -> I {
|
||||
addi(dest, src, 0)
|
||||
}
|
||||
9
src/compiler/riscv64/instruction/opcode.rs
Normal file
9
src/compiler/riscv64/instruction/opcode.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub const OPCODE_MASK: u32 = 0b1111111;
|
||||
|
||||
pub const SYSTEM: u32 = 0b1110011;
|
||||
pub const LOAD : u32 = 0b0000011;
|
||||
pub const STORE : u32 = 0b0100011;
|
||||
pub const AUIPC : u32 = 0b0010111;
|
||||
pub const IMM_OP: u32 = 0b0010011;
|
||||
pub const OP : u32 = 0b0110011;
|
||||
pub const JAL : u32 = 0b1101111;
|
||||
93
src/compiler/riscv64/instruction/reg.rs
Normal file
93
src/compiler/riscv64/instruction/reg.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
pub struct Reg(u8);
|
||||
|
||||
/// hard wired 0
|
||||
pub const zero: Reg = Reg(0);
|
||||
/// return address
|
||||
pub const ra: Reg = Reg(1);
|
||||
/// stack pointer
|
||||
pub const sp: Reg = Reg(2);
|
||||
/// global pointer
|
||||
pub const gp: Reg = Reg(3);
|
||||
/// thread pointer
|
||||
pub const tp: Reg = Reg(4);
|
||||
/// temp / alternate link
|
||||
pub const t0: Reg = Reg(5);
|
||||
pub const t1: Reg = Reg(6);
|
||||
pub const t2: Reg = Reg(7);
|
||||
|
||||
pub const fp: Reg = Reg(8);
|
||||
pub const s0: Reg = Reg(8);
|
||||
pub const s1: Reg = Reg(9);
|
||||
|
||||
pub const a0: Reg = Reg(10);
|
||||
pub const a1: Reg = Reg(11);
|
||||
pub const a2: Reg = Reg(12);
|
||||
pub const a3: Reg = Reg(13);
|
||||
pub const a4: Reg = Reg(14);
|
||||
pub const a5: Reg = Reg(15);
|
||||
pub const a6: Reg = Reg(16);
|
||||
pub const a7: Reg = Reg(17);
|
||||
|
||||
pub const s2: Reg = Reg(18);
|
||||
pub const s3: Reg = Reg(19);
|
||||
pub const s4: Reg = Reg(20);
|
||||
pub const s5: Reg = Reg(21);
|
||||
pub const s6: Reg = Reg(22);
|
||||
pub const s7: Reg = Reg(23);
|
||||
pub const s8: Reg = Reg(24);
|
||||
pub const s9: Reg = Reg(25);
|
||||
pub const s10: Reg = Reg(26);
|
||||
pub const s11: Reg = Reg(27);
|
||||
|
||||
pub const t3: Reg = Reg(28);
|
||||
pub const t4: Reg = Reg(29);
|
||||
pub const t5: Reg = Reg(30);
|
||||
pub const t6: Reg = Reg(31);
|
||||
|
||||
|
||||
|
||||
pub const ft0: Reg = Reg(0);
|
||||
pub const ft1: Reg = Reg(1);
|
||||
pub const ft2: Reg = Reg(2);
|
||||
pub const ft3: Reg = Reg(3);
|
||||
pub const ft4: Reg = Reg(4);
|
||||
pub const ft5: Reg = Reg(5);
|
||||
pub const ft6: Reg = Reg(6);
|
||||
pub const ft7: Reg = Reg(7);
|
||||
|
||||
pub const fs0: Reg = Reg(8);
|
||||
pub const fs1: Reg = Reg(9);
|
||||
|
||||
pub const fa0: Reg = Reg(10);
|
||||
pub const fa1: Reg = Reg(11);
|
||||
pub const fa2: Reg = Reg(12);
|
||||
pub const fa3: Reg = Reg(13);
|
||||
pub const fa4: Reg = Reg(14);
|
||||
pub const fa5: Reg = Reg(15);
|
||||
pub const fa6: Reg = Reg(16);
|
||||
pub const fa7: Reg = Reg(17);
|
||||
|
||||
pub const fs2: Reg = Reg(18);
|
||||
pub const fs3: Reg = Reg(19);
|
||||
pub const fs4: Reg = Reg(20);
|
||||
pub const fs5: Reg = Reg(21);
|
||||
pub const fs6: Reg = Reg(22);
|
||||
pub const fs7: Reg = Reg(23);
|
||||
pub const fs8: Reg = Reg(24);
|
||||
pub const fs9: Reg = Reg(25);
|
||||
pub const fs10: Reg = Reg(26);
|
||||
pub const fs11: Reg = Reg(27);
|
||||
|
||||
pub const ft8: Reg = Reg(28);
|
||||
pub const ft9: Reg = Reg(29);
|
||||
pub const ft10: Reg = Reg(30);
|
||||
pub const ft11: Reg = Reg(31);
|
||||
|
||||
impl Reg {
|
||||
#[inline]
|
||||
pub const fn val(&self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
}
|
||||
33
src/compiler/riscv64/mod.rs
Normal file
33
src/compiler/riscv64/mod.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use crate::compiler::program::Instr;
|
||||
mod elf;
|
||||
mod instruction;
|
||||
mod asm;
|
||||
|
||||
use instruction::*;
|
||||
|
||||
pub fn gen() -> Vec<u8> {
|
||||
let mut program = Vec::new();
|
||||
let msg = b"Hello world!\n";
|
||||
program.extend(msg);
|
||||
program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
|
||||
let start = program.len() as u64;
|
||||
let instructions = [
|
||||
auipc(t0, 0),
|
||||
addi(t0, t0, -(start as i32)),
|
||||
addi(a0, zero, 1),
|
||||
mv(a1, t0),
|
||||
addi(a2, zero, msg.len() as i32),
|
||||
addi(a7, zero, 64),
|
||||
addi(t0, zero, 200),
|
||||
ecall(),
|
||||
// exit
|
||||
addi(a0, zero, 0),
|
||||
addi(a7, zero, 93),
|
||||
ecall(),
|
||||
j(0),
|
||||
];
|
||||
for i in instructions {
|
||||
program.extend(i.to_le_bytes());
|
||||
}
|
||||
elf::create(program, start)
|
||||
}
|
||||
7
src/compiler/target.rs
Normal file
7
src/compiler/target.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub trait Target {
|
||||
type Reg;
|
||||
}
|
||||
|
||||
pub trait RegType {
|
||||
type Size;
|
||||
}
|
||||
16
src/main.rs
16
src/main.rs
@@ -1,17 +1,11 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(const_unbounded_shifts)]
|
||||
#![feature(unbounded_shifts)]
|
||||
|
||||
mod util;
|
||||
mod v1;
|
||||
mod v2;
|
||||
mod compiler;
|
||||
mod parser;
|
||||
|
||||
fn main() {
|
||||
let arg = std::env::args_os().nth(1);
|
||||
if let Some(path) = arg {
|
||||
let file = std::fs::read_to_string(path).expect("failed to read file");
|
||||
println!("{file}");
|
||||
v1::parse_file(&file);
|
||||
// v2::parse_file(&file);
|
||||
} else {
|
||||
v1::run_stdin();
|
||||
}
|
||||
compiler::main();
|
||||
}
|
||||
|
||||
14
src/parser/mod.rs
Normal file
14
src/parser/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
mod v1;
|
||||
mod v2;
|
||||
|
||||
pub fn main() {
|
||||
let arg = std::env::args_os().nth(1);
|
||||
if let Some(path) = arg {
|
||||
let file = std::fs::read_to_string(path).expect("failed to read file");
|
||||
println!("{file}");
|
||||
v1::parse_file(&file);
|
||||
// v2::parse_file(&file);
|
||||
} else {
|
||||
v1::run_stdin();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::token::{Symbol, Token};
|
||||
use super::{Body, Node, Parsable, ParserError, ParserErrors, TokenCursor, Val};
|
||||
use super::{Body, Node, Parsable, ParserError, ParserErrors, TokenCursor, Literal};
|
||||
|
||||
pub type ExprNode = Node<Box<Expr>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Expr {
|
||||
Val(Node<Val>),
|
||||
Lit(Node<Literal>),
|
||||
Ident(String),
|
||||
BinaryOp(Operator, ExprNode, ExprNode),
|
||||
Block(Node<Body>),
|
||||
@@ -30,7 +30,7 @@ pub enum Operator {
|
||||
impl Expr {
|
||||
pub fn ended_with_error(&self) -> bool {
|
||||
match self {
|
||||
Expr::Val(_) => false,
|
||||
Expr::Lit(_) => false,
|
||||
Expr::Ident(_) => false,
|
||||
Expr::BinaryOp(_, _, e) => e.is_err() || e.as_ref().is_ok_and(|e| e.ended_with_error()),
|
||||
Expr::Block(b) => b.is_err(),
|
||||
@@ -48,8 +48,8 @@ impl Parsable for Expr {
|
||||
cursor.next();
|
||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||
cursor.next();
|
||||
return Ok(Expr::Val(Node::new(
|
||||
Val::Unit,
|
||||
return Ok(Expr::Lit(Node::new(
|
||||
Literal::Unit,
|
||||
cursor.next_pos().char_span(),
|
||||
)));
|
||||
}
|
||||
@@ -62,7 +62,7 @@ impl Parsable for Expr {
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
Self::Block(Node::parse(cursor, errors))
|
||||
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
||||
Self::Val(val)
|
||||
Self::Lit(val)
|
||||
} else {
|
||||
let next = cursor.peek().unwrap();
|
||||
match &next.token {
|
||||
@@ -172,7 +172,7 @@ impl Operator {
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expr::Val(c) => c.fmt(f)?,
|
||||
Expr::Lit(c) => c.fmt(f)?,
|
||||
Expr::Ident(n) => f.write_str(n)?,
|
||||
Expr::Block(b) => b.fmt(f)?,
|
||||
Expr::BinaryOp(op, e1, e2) => {
|
||||
@@ -1,8 +1,22 @@
|
||||
use std::io::{stdout, BufRead, BufReader};
|
||||
|
||||
mod parser;
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod module;
|
||||
mod node;
|
||||
mod token;
|
||||
mod val;
|
||||
|
||||
use parser::{Module, Node, ParserErrors, Statement, TokenCursor};
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
pub use module::*;
|
||||
pub use node::*;
|
||||
pub use val::*;
|
||||
use token::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
let mut errors = ParserErrors::new();
|
||||
@@ -10,8 +24,9 @@ pub fn parse_file(file: &str) {
|
||||
if let Ok(module) = node.as_ref() {
|
||||
println!("{module:#?}");
|
||||
};
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(&mut stdout(), file).unwrap();
|
||||
err.write_for(out, file).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +35,12 @@ pub fn run_stdin() {
|
||||
let mut errors = ParserErrors::new();
|
||||
let str = &line.expect("failed to read line");
|
||||
let mut cursor = TokenCursor::from(&str[..]);
|
||||
let out = &mut stdout();
|
||||
if let Ok(expr) = Node::<Statement>::parse(&mut cursor, &mut errors).as_ref() {
|
||||
println!("{:?}", expr);
|
||||
}
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(&mut stdout(), str).unwrap();
|
||||
err.write_for(out, str).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod token;
|
||||
mod val;
|
||||
mod node;
|
||||
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
pub use val::*;
|
||||
pub use node::*;
|
||||
|
||||
use token::*;
|
||||
use super::{token::*, Body, Node, Parsable, ParserError, ParserErrors, TokenCursor};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
@@ -2,7 +2,7 @@ use super::{CharCursor, MaybeParsable, ParserError, ParserErrors, Symbol, Token,
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Val {
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
Char(char),
|
||||
Number(Number),
|
||||
@@ -16,7 +16,7 @@ pub struct Number {
|
||||
pub ty: Option<String>,
|
||||
}
|
||||
|
||||
impl MaybeParsable for Val {
|
||||
impl MaybeParsable for Literal {
|
||||
fn maybe_parse(cursor: &mut TokenCursor, _: &mut ParserErrors) -> Result<Option<Self>, ParserError> {
|
||||
let inst = cursor.expect_peek()?;
|
||||
let mut res = match &inst.token {
|
||||
@@ -85,7 +85,7 @@ pub fn string_from(cursor: &mut CharCursor) -> Result<String, ParserError> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Val {
|
||||
impl Debug for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(str) => str.fmt(f),
|
||||
@@ -1,8 +1,17 @@
|
||||
use std::{ffi::OsStr, io::{BufRead, BufReader}};
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
use parser::{print_error, CharCursor, Module, Statement};
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod module;
|
||||
mod util;
|
||||
|
||||
mod parser;
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
pub use module::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
match Module::parse(&mut CharCursor::from(file)) {
|
||||
@@ -1,16 +1,5 @@
|
||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod util;
|
||||
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
use util::WHITESPACE_SET;
|
||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
120
src/util/bits.rs
Normal file
120
src/util/bits.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
pub const fn u(x: i32) -> u32 {
|
||||
unsafe { std::mem::transmute(x) }
|
||||
}
|
||||
|
||||
pub const fn low_mask(high: u32, low: u32) -> u32 {
|
||||
(2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||
}
|
||||
|
||||
pub const fn mask(high: u32, low: u32) -> u32 {
|
||||
low_mask(high, low).unbounded_shl(low)
|
||||
}
|
||||
|
||||
pub const fn bits(x: i32, high: u32, low: u32) -> u32 {
|
||||
let x = u(x);
|
||||
x.unbounded_shr(low) & low_mask(high, low)
|
||||
}
|
||||
|
||||
pub const fn bit(x: i32, i: u32) -> u32 {
|
||||
let x = u(x);
|
||||
x.unbounded_shr(i) & 2u32 << i
|
||||
}
|
||||
|
||||
pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
||||
if x < 0 {
|
||||
if high == low {
|
||||
return false;
|
||||
}
|
||||
(bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||
} else {
|
||||
bits(x, high, low) << low == u(x)
|
||||
}
|
||||
}
|
||||
|
||||
// use std::ops::{Add, Shl, Shr};
|
||||
//
|
||||
// pub const fn u(x: i32) -> u32 {
|
||||
// unsafe { std::mem::transmute(x) }
|
||||
// }
|
||||
//
|
||||
// pub const fn low_mask(high: u8, low: u8) -> u32 {
|
||||
// (2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||
// }
|
||||
//
|
||||
// pub const fn mask(high: u8, low: u8) -> u32 {
|
||||
// low_mask(high, low).unbounded_shl(low)
|
||||
// }
|
||||
//
|
||||
// pub const fn bits(x: i32, high: u8, low: u8) -> u32 {
|
||||
// let x = u(x);
|
||||
// x.unbounded_shr(low) & low_mask(high, low)
|
||||
// }
|
||||
//
|
||||
// pub const fn bit(x: i32, i: u32) -> u32 {
|
||||
// let x = u(x);
|
||||
// x.unbounded_shr(i) & 2u32 << i
|
||||
// }
|
||||
//
|
||||
// pub const fn in_bit_range<T: Shl<u8, Output = T> + Shr<u8, Output = T>>(
|
||||
// x: T,
|
||||
// high: u8,
|
||||
// low: u8,
|
||||
// ) -> bool {
|
||||
// if x < 0 {
|
||||
// if high == low {
|
||||
// return false;
|
||||
// }
|
||||
// (bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||
// } else {
|
||||
// bits(x, high, low) << low == u(x)
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub struct Bits<
|
||||
// T: Shl<u8, Output = T> + Shr<u8, Output = T>,
|
||||
// const S: bool,
|
||||
// const H: u8,
|
||||
// const L: u8,
|
||||
// >(T);
|
||||
// pub struct U32Bits<const H: u32, const L: u32>(u32);
|
||||
//
|
||||
// impl<T: Shl<u8, Output = T> + Shr<u8, Output = T> + Add<u8, Output = T>, const S: bool, const H: u8, const L: u8>
|
||||
// Bits<T, S, H, L>
|
||||
// {
|
||||
// pub const fn new(val: T) -> Self {
|
||||
// assert!(in_bit_range(val, H, L));
|
||||
// Self(val + L)
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn get_bits() {
|
||||
assert_eq!(bits(0b10111010, 5, 3), 0b111);
|
||||
assert_eq!(bits(0b10111010, 7, 5), 0b101);
|
||||
assert_eq!(bits(0b10111010, 2, 0), 0b010);
|
||||
assert_eq!(bits(0b10111010, 7, 7), 0b1);
|
||||
assert_eq!(bits(0b1, 0, 0), 0b1);
|
||||
assert_eq!(bits(0b1, 1, 1), 0b0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range() {
|
||||
assert!(!in_bit_range(0b00111100, 5, 3));
|
||||
assert!(!in_bit_range(0b00111100, 4, 2));
|
||||
assert!(in_bit_range(0b000111100, 5, 2));
|
||||
assert!(in_bit_range(0b000000001, 0, 0));
|
||||
assert!(in_bit_range(0b000001000, 3, 3));
|
||||
|
||||
assert!(!in_bit_range(-3, 1, 0));
|
||||
assert!(!in_bit_range(-5, 2, 2));
|
||||
assert!(!in_bit_range(-5, 4, 3));
|
||||
assert!(in_bit_range(-1, 1, 0));
|
||||
assert!(in_bit_range(-4, 2, 0));
|
||||
assert!(in_bit_range(-5, 3, 2));
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,5 @@
|
||||
use core::fmt;
|
||||
mod padder;
|
||||
mod bits;
|
||||
|
||||
pub struct Padder<'buf> {
|
||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||
on_newline: bool,
|
||||
}
|
||||
|
||||
impl fmt::Write for Padder<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for s in s.split_inclusive('\n') {
|
||||
if self.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
|
||||
self.on_newline = s.ends_with('\n');
|
||||
self.buf.write_str(s)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
if self.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.on_newline = c == '\n';
|
||||
self.buf.write_char(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'buf> Padder<'buf> {
|
||||
pub fn new(buf: &'buf mut (dyn fmt::Write + 'buf)) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
on_newline: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use padder::*;
|
||||
pub use bits::*;
|
||||
|
||||
38
src/util/padder.rs
Normal file
38
src/util/padder.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use core::fmt;
|
||||
|
||||
pub struct Padder<'buf> {
|
||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||
on_newline: bool,
|
||||
}
|
||||
|
||||
impl fmt::Write for Padder<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for s in s.split_inclusive('\n') {
|
||||
if self.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
|
||||
self.on_newline = s.ends_with('\n');
|
||||
self.buf.write_str(s)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
if self.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
self.on_newline = c == '\n';
|
||||
self.buf.write_char(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'buf> Padder<'buf> {
|
||||
pub fn new(buf: &'buf mut (dyn fmt::Write + 'buf)) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
on_newline: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user