establishing some syntax
This commit is contained in:
parent
433f2db4d1
commit
09aacff161
55
Cargo.lock
generated
55
Cargo.lock
generated
|
@ -83,6 +83,12 @@ dependencies = [
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "beef"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
@ -167,6 +173,12 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
|
@ -197,12 +209,17 @@ dependencies = [
|
||||||
name = "hbbytecode"
|
name = "hbbytecode"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"paste",
|
||||||
"with_builtin_macros",
|
"with_builtin_macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hblang"
|
name = "hblang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"hbvm",
|
||||||
|
"logos",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hbvm"
|
name = "hbvm"
|
||||||
|
@ -254,6 +271,38 @@ version = "0.2.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
|
||||||
|
dependencies = [
|
||||||
|
"logos-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-codegen"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
|
||||||
|
dependencies = [
|
||||||
|
"beef",
|
||||||
|
"fnv",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex-syntax",
|
||||||
|
"syn 2.0.38",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-derive"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
|
||||||
|
dependencies = [
|
||||||
|
"logos-codegen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
|
@ -349,6 +398,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "1.16.2"
|
version = "1.16.2"
|
||||||
|
|
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
paste = "1.0.14"
|
||||||
with_builtin_macros = "0.0.3"
|
with_builtin_macros = "0.0.3"
|
||||||
|
|
|
@ -23,6 +23,21 @@ macro_rules! define_items {
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct $name($(pub $item),*);
|
pub struct $name($(pub $item),*);
|
||||||
unsafe impl BytecodeItem for $name {}
|
unsafe impl BytecodeItem for $name {}
|
||||||
|
|
||||||
|
impl Encodable for $name {
|
||||||
|
fn encode(self, buffer: &mut impl Buffer) {
|
||||||
|
let array = unsafe {
|
||||||
|
core::mem::transmute::<Self, [u8; core::mem::size_of::<Self>()]>(self)
|
||||||
|
};
|
||||||
|
for byte in array {
|
||||||
|
unsafe { buffer.write(byte) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_len(self) -> usize {
|
||||||
|
core::mem::size_of::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -85,13 +100,55 @@ unsafe impl BytecodeItem for u8 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Buffer {
|
||||||
|
fn reserve(&mut self, bytes: usize);
|
||||||
|
/// # Safety
|
||||||
|
/// Reserve needs to be called before this function, and only reserved amount can be written.
|
||||||
|
unsafe fn write(&mut self, byte: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Encodable {
|
||||||
|
fn encode(self, buffer: &mut impl Buffer);
|
||||||
|
fn encode_len(self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! gen_opcodes {
|
macro_rules! gen_opcodes {
|
||||||
($($opcode:expr, $mnemonic:ident, $_ty:ident, $doc:literal;)*) => {
|
($($opcode:expr, $mnemonic:ident, $ty:ident, $doc:literal;)*) => {
|
||||||
pub mod opcode {
|
pub mod opcode {
|
||||||
$(
|
$(
|
||||||
#[doc = $doc]
|
#[doc = $doc]
|
||||||
pub const $mnemonic: u8 = $opcode;
|
pub const $mnemonic: u8 = $opcode;
|
||||||
)*
|
)*
|
||||||
|
|
||||||
|
paste::paste! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Op { $(
|
||||||
|
[< $mnemonic:lower:camel >](super::[<Ops $ty>]),
|
||||||
|
)* }
|
||||||
|
|
||||||
|
impl crate::Encodable for Op {
|
||||||
|
fn encode(self, buffer: &mut impl crate::Buffer) {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::[< $mnemonic:lower:camel >](op) => {
|
||||||
|
unsafe { buffer.write($opcode) };
|
||||||
|
op.encode(buffer);
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_len(self) -> usize {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::[< $mnemonic:lower:camel >](op) => {
|
||||||
|
1 + crate::Encodable::encode_len(op)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,8 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
#hbbytecode = { version = "0.1.0", path = "../hbbytecode" }
|
||||||
|
logos = "0.13.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hbvm = { path = "../hbvm", features = ["nightly"] }
|
||||||
|
|
149
hblang/src/lexer.rs
Normal file
149
hblang/src/lexer.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
macro_rules! gen_token {
|
||||||
|
($name:ident {
|
||||||
|
keywords: {
|
||||||
|
$($keyword:ident = $lit:literal,)*
|
||||||
|
},
|
||||||
|
operators: $op_name:ident {
|
||||||
|
$($prec:literal: {$(
|
||||||
|
$op:ident = $op_lit:literal,
|
||||||
|
)*},)*
|
||||||
|
},
|
||||||
|
types: $ty_type:ident {
|
||||||
|
$($ty:ident = $ty_lit:literal,)*
|
||||||
|
},
|
||||||
|
regexes: {
|
||||||
|
$($regex:ident = $regex_lit:literal,)*
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, Logos)]
|
||||||
|
#[logos(skip "[ \t\n]+")]
|
||||||
|
pub enum $name {
|
||||||
|
$(#[token($lit)] $keyword,)*
|
||||||
|
$($(#[token($op_lit, |_| $op_name::$op)])*)*
|
||||||
|
Op($op_name),
|
||||||
|
$(#[token($ty_lit, |_| $ty_type::$ty)])*
|
||||||
|
Ty($ty_type),
|
||||||
|
$(#[regex($regex_lit)] $regex,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
|
pub enum $op_name {
|
||||||
|
$($($op,)*)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
|
pub enum $ty_type {
|
||||||
|
$($ty,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $op_name {
|
||||||
|
pub fn prec(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
$($($op_name::$op => $prec,)*)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_token! {
|
||||||
|
TokenKind {
|
||||||
|
keywords: {
|
||||||
|
Fn = "fn",
|
||||||
|
Let = "let",
|
||||||
|
If = "if",
|
||||||
|
Else = "else",
|
||||||
|
For = "for",
|
||||||
|
Return = "return",
|
||||||
|
Break = "break",
|
||||||
|
Continue = "continue",
|
||||||
|
Struct = "struct",
|
||||||
|
|
||||||
|
True = "true",
|
||||||
|
False = "false",
|
||||||
|
|
||||||
|
LBrace = "{",
|
||||||
|
RBrace = "}",
|
||||||
|
LParen = "(",
|
||||||
|
RParen = ")",
|
||||||
|
LBracket = "[",
|
||||||
|
RBracket = "]",
|
||||||
|
|
||||||
|
Colon = ":",
|
||||||
|
Semicolon = ";",
|
||||||
|
Comma = ",",
|
||||||
|
Dot = ".",
|
||||||
|
},
|
||||||
|
operators: Op {
|
||||||
|
14: {
|
||||||
|
Assign = "=",
|
||||||
|
AddAssign = "+=",
|
||||||
|
SubAssign = "-=",
|
||||||
|
MulAssign = "*=",
|
||||||
|
DivAssign = "/=",
|
||||||
|
ModAssign = "%=",
|
||||||
|
AndAssign = "&=",
|
||||||
|
OrAssign = "|=",
|
||||||
|
XorAssign = "^=",
|
||||||
|
ShlAssign = "<<=",
|
||||||
|
ShrAssign = ">>=",
|
||||||
|
},
|
||||||
|
12: {
|
||||||
|
Or = "||",
|
||||||
|
},
|
||||||
|
11: {
|
||||||
|
And = "&&",
|
||||||
|
},
|
||||||
|
10: {
|
||||||
|
Bor = "|",
|
||||||
|
},
|
||||||
|
9: {
|
||||||
|
Xor = "^",
|
||||||
|
},
|
||||||
|
8: {
|
||||||
|
Band = "&",
|
||||||
|
},
|
||||||
|
7: {
|
||||||
|
Eq = "==",
|
||||||
|
Neq = "!=",
|
||||||
|
},
|
||||||
|
6: {
|
||||||
|
Lt = "<",
|
||||||
|
Gt = ">",
|
||||||
|
Le = "<=",
|
||||||
|
Ge = ">=",
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
Shl = "<<",
|
||||||
|
Shr = ">>",
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
Add = "+",
|
||||||
|
Sub = "-",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
Mul = "*",
|
||||||
|
Div = "/",
|
||||||
|
Mod = "%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
types: Ty {
|
||||||
|
U8 = "u8",
|
||||||
|
U16 = "u16",
|
||||||
|
U32 = "u32",
|
||||||
|
U64 = "u64",
|
||||||
|
I8 = "i8",
|
||||||
|
I16 = "i16",
|
||||||
|
I32 = "i32",
|
||||||
|
I64 = "i64",
|
||||||
|
Bool = "bool",
|
||||||
|
Void = "void",
|
||||||
|
},
|
||||||
|
regexes: {
|
||||||
|
Ident = "[a-zA-Z_][a-zA-Z0-9_]*",
|
||||||
|
Number = "[0-9]+",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,2 @@
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
mod lexer;
|
||||||
left + right
|
mod parser;
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
359
hblang/src/parser.rs
Normal file
359
hblang/src/parser.rs
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
use {core::panic, std::iter};
|
||||||
|
|
||||||
|
use logos::{Lexer, Logos};
|
||||||
|
|
||||||
|
use crate::lexer::{Op, TokenKind, Ty};
|
||||||
|
|
||||||
|
pub enum Item {
|
||||||
|
Struct(Struct),
|
||||||
|
Function(Function),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Type {
|
||||||
|
Builtin(Ty),
|
||||||
|
Struct(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Struct {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Field {
|
||||||
|
pub name: String,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Function {
|
||||||
|
pub name: String,
|
||||||
|
pub args: Vec<Arg>,
|
||||||
|
pub ret: Type,
|
||||||
|
pub body: Vec<Exp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Arg {
|
||||||
|
pub name: String,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Exp {
|
||||||
|
Literal(Literal),
|
||||||
|
Variable(String),
|
||||||
|
Call {
|
||||||
|
name: Box<Exp>,
|
||||||
|
args: Vec<Exp>,
|
||||||
|
},
|
||||||
|
Index {
|
||||||
|
base: Box<Exp>,
|
||||||
|
index: Box<Exp>,
|
||||||
|
},
|
||||||
|
Field {
|
||||||
|
base: Box<Exp>,
|
||||||
|
field: String,
|
||||||
|
},
|
||||||
|
Unary {
|
||||||
|
op: Op,
|
||||||
|
exp: Box<Exp>,
|
||||||
|
},
|
||||||
|
Binary {
|
||||||
|
op: Op,
|
||||||
|
left: Box<Exp>,
|
||||||
|
right: Box<Exp>,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
cond: Box<Exp>,
|
||||||
|
then: Box<Exp>,
|
||||||
|
else_: Option<Box<Exp>>,
|
||||||
|
},
|
||||||
|
Block(Vec<Exp>),
|
||||||
|
Return(Box<Exp>),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Literal {
|
||||||
|
Int(i64),
|
||||||
|
Bool(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Token {
|
||||||
|
pub kind: TokenKind,
|
||||||
|
pub span: std::ops::Range<usize>,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser<'a> {
|
||||||
|
next_token: Option<Token>,
|
||||||
|
lexer: logos::Lexer<'a, TokenKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
pub fn new(input: &'a str) -> Self {
|
||||||
|
let mut lexer = TokenKind::lexer(input);
|
||||||
|
let next_token = Self::next_token(&mut lexer);
|
||||||
|
Self { next_token, lexer }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<Token> {
|
||||||
|
let token = self.next_token.clone();
|
||||||
|
self.next_token = Self::next_token(&mut self.lexer);
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_token(lexer: &mut Lexer<TokenKind>) -> Option<Token> {
|
||||||
|
lexer.next().map(|r| {
|
||||||
|
r.map(|e| Token {
|
||||||
|
kind: e,
|
||||||
|
span: lexer.span(),
|
||||||
|
value: lexer.slice().to_owned(),
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
let (line, col) = Self::pos_to_line_col_low(lexer.source(), lexer.span().start);
|
||||||
|
panic!("Lexer error: {}:{}", line, col,)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_to_line_col(&self, pos: usize) -> (usize, usize) {
|
||||||
|
Self::pos_to_line_col_low(self.lexer.source(), pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_to_line_col_low(source: &str, pos: usize) -> (usize, usize) {
|
||||||
|
let line = source[..pos].lines().count();
|
||||||
|
let col = source[..pos].lines().last().map(|l| l.len()).unwrap_or(0);
|
||||||
|
(line, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect(&mut self, kind: TokenKind) -> Token {
|
||||||
|
let token = self.next().unwrap_or_else(|| panic!("Unexpected EOF"));
|
||||||
|
if token.kind == kind {
|
||||||
|
token
|
||||||
|
} else {
|
||||||
|
let (line, col) = self.pos_to_line_col(token.span.start);
|
||||||
|
panic!(
|
||||||
|
"Expected {:?} at {}:{}, found {:?}",
|
||||||
|
kind, line, col, token.kind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self) -> Option<&Token> {
|
||||||
|
self.next_token.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_advance(&mut self, kind: TokenKind) -> bool {
|
||||||
|
if self.peek().is_some_and(|t| t.kind == kind) {
|
||||||
|
self.next();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Vec<Item> {
|
||||||
|
iter::from_fn(|| self.parse_item()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_item(&mut self) -> Option<Item> {
|
||||||
|
let token = self.next()?;
|
||||||
|
match token.kind {
|
||||||
|
TokenKind::Struct => Some(self.parse_struct()),
|
||||||
|
TokenKind::Fn => Some(self.parse_function()),
|
||||||
|
tkn => {
|
||||||
|
let (line, col) = self.pos_to_line_col(token.span.start);
|
||||||
|
panic!("Unexpected {:?} at {}:{}", tkn, line, col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_struct(&mut self) -> Item {
|
||||||
|
let name = self.expect(TokenKind::Ident).value;
|
||||||
|
self.expect(TokenKind::LBrace);
|
||||||
|
let fields = iter::from_fn(|| self.parse_field()).collect();
|
||||||
|
self.expect(TokenKind::RBrace);
|
||||||
|
Item::Struct(Struct { name, fields })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_field(&mut self) -> Option<Field> {
|
||||||
|
if self.peek()?.kind == TokenKind::RBrace {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = self.expect(TokenKind::Ident).value;
|
||||||
|
self.expect(TokenKind::Colon);
|
||||||
|
let ty = self.type_();
|
||||||
|
self.try_advance(TokenKind::Comma);
|
||||||
|
|
||||||
|
Some(Field { name, ty })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_(&mut self) -> Type {
|
||||||
|
let token = self.next().unwrap();
|
||||||
|
match token.kind {
|
||||||
|
TokenKind::Ty(ty) => Type::Builtin(ty),
|
||||||
|
TokenKind::Ident => Type::Struct(token.value),
|
||||||
|
tkn => {
|
||||||
|
let (line, col) = self.pos_to_line_col(token.span.start);
|
||||||
|
panic!("Unexpected {:?} at {}:{}", tkn, line, col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_function(&mut self) -> Item {
|
||||||
|
let name = self.expect(TokenKind::Ident).value;
|
||||||
|
self.expect(TokenKind::LParen);
|
||||||
|
let args = iter::from_fn(|| self.parse_arg()).collect();
|
||||||
|
self.expect(TokenKind::RParen);
|
||||||
|
self.expect(TokenKind::Colon);
|
||||||
|
let ret = self.type_();
|
||||||
|
self.expect(TokenKind::LBrace);
|
||||||
|
let body = iter::from_fn(|| self.parse_stmt()).collect();
|
||||||
|
self.expect(TokenKind::RBrace);
|
||||||
|
Item::Function(Function {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
ret,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arg(&mut self) -> Option<Arg> {
|
||||||
|
if self.peek()?.kind == TokenKind::RParen {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = self.expect(TokenKind::Ident).value;
|
||||||
|
self.expect(TokenKind::Colon);
|
||||||
|
let ty = self.type_();
|
||||||
|
self.try_advance(TokenKind::Comma);
|
||||||
|
|
||||||
|
Some(Arg { name, ty })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_stmt(&mut self) -> Option<Exp> {
|
||||||
|
if self.peek()?.kind == TokenKind::RBrace {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
self.expect(TokenKind::Semicolon);
|
||||||
|
|
||||||
|
Some(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr(&mut self) -> Exp {
|
||||||
|
self.parse_binary_expr(255)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_binary_expr(&mut self, min_prec: u8) -> Exp {
|
||||||
|
let mut lhs = self.parse_unit_expr();
|
||||||
|
|
||||||
|
while let Some(TokenKind::Op(op)) = self.peek().map(|t| t.kind) {
|
||||||
|
let prec = op.prec();
|
||||||
|
if prec <= min_prec {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next();
|
||||||
|
let rhs = self.parse_binary_expr(prec);
|
||||||
|
|
||||||
|
lhs = Exp::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(lhs),
|
||||||
|
right: Box::new(rhs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_unit_expr(&mut self) -> Exp {
|
||||||
|
let token = self.next().unwrap();
|
||||||
|
let mut expr = match token.kind {
|
||||||
|
TokenKind::True => Exp::Literal(Literal::Bool(true)),
|
||||||
|
TokenKind::False => Exp::Literal(Literal::Bool(false)),
|
||||||
|
TokenKind::Ident => Exp::Variable(token.value),
|
||||||
|
TokenKind::LBrace => {
|
||||||
|
let body = iter::from_fn(|| self.parse_stmt()).collect();
|
||||||
|
self.expect(TokenKind::RBrace);
|
||||||
|
Exp::Block(body)
|
||||||
|
}
|
||||||
|
TokenKind::LParen => {
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
self.expect(TokenKind::RParen);
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
TokenKind::Number => {
|
||||||
|
let value = token.value.parse().unwrap();
|
||||||
|
Exp::Literal(Literal::Int(value))
|
||||||
|
}
|
||||||
|
TokenKind::Fn => todo!(),
|
||||||
|
TokenKind::Let => todo!(),
|
||||||
|
TokenKind::If => todo!(),
|
||||||
|
TokenKind::Else => todo!(),
|
||||||
|
TokenKind::For => todo!(),
|
||||||
|
TokenKind::Return => todo!(),
|
||||||
|
TokenKind::Break => todo!(),
|
||||||
|
TokenKind::Continue => todo!(),
|
||||||
|
TokenKind::Struct => todo!(),
|
||||||
|
TokenKind::RBrace => todo!(),
|
||||||
|
TokenKind::RParen => todo!(),
|
||||||
|
TokenKind::LBracket => todo!(),
|
||||||
|
TokenKind::RBracket => todo!(),
|
||||||
|
TokenKind::Colon => todo!(),
|
||||||
|
TokenKind::Semicolon => todo!(),
|
||||||
|
TokenKind::Comma => todo!(),
|
||||||
|
TokenKind::Op(_) => todo!(),
|
||||||
|
TokenKind::Ty(_) => todo!(),
|
||||||
|
TokenKind::Dot => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.peek().map(|t| t.kind) {
|
||||||
|
Some(TokenKind::LParen) => {
|
||||||
|
self.next();
|
||||||
|
let args = iter::from_fn(|| self.parse_call_arg()).collect();
|
||||||
|
self.expect(TokenKind::RParen);
|
||||||
|
expr = Exp::Call {
|
||||||
|
name: Box::new(expr),
|
||||||
|
args,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(TokenKind::LBracket) => {
|
||||||
|
self.next();
|
||||||
|
let index = self.parse_expr();
|
||||||
|
self.expect(TokenKind::RBracket);
|
||||||
|
expr = Exp::Index {
|
||||||
|
base: Box::new(expr),
|
||||||
|
index: Box::new(index),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(TokenKind::Dot) => {
|
||||||
|
self.next();
|
||||||
|
let field = self.expect(TokenKind::Ident).value;
|
||||||
|
expr = Exp::Field {
|
||||||
|
base: Box::new(expr),
|
||||||
|
field,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => break expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_call_arg(&mut self) -> Option<Exp> {
|
||||||
|
if self.peek()?.kind == TokenKind::RParen {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
self.try_advance(TokenKind::Comma);
|
||||||
|
|
||||||
|
Some(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(input: &str) -> Vec<Item> {
|
||||||
|
Parser::new(input).parse()
|
||||||
|
}
|
Loading…
Reference in a new issue