cleanup code and cargo fmt
This commit is contained in:
parent
8f06f8a502
commit
2392087c4b
12 changed files with 353 additions and 193 deletions
36
print3.wat
Normal file
36
print3.wat
Normal file
|
@ -0,0 +1,36 @@
|
|||
;; testing offsets and such
|
||||
|
||||
(module
|
||||
;; import from wasi
|
||||
;; fn fd_write(fd, *iovs, iovs_len, nwritten) -> bytes_written
|
||||
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
|
||||
|
||||
;; create memory (size = 1 page = 64KiB)
|
||||
(memory $foobar 1)
|
||||
|
||||
;; export memory - it's required, but we don't use it so the size is set to 0
|
||||
(export "memory" (memory 0))
|
||||
|
||||
;; write string to memory (offset = 8 bytes)
|
||||
(data (i32.const 20) "Hello, world!\n")
|
||||
|
||||
(func $main (export "_start")
|
||||
;; iov.iov_base - pointer to string (offset = 0 bytes)
|
||||
;; the string's offset is 8 bytes in memory
|
||||
(i32.store (i32.const 12) (i32.const 20))
|
||||
|
||||
;; iov.iov_len - length of the hello world string (offset = 4 bytes)
|
||||
;; the string's length is 14 bytes
|
||||
(i32.store (i32.const 16) (i32.const 24))
|
||||
|
||||
(i32.const 1)
|
||||
|
||||
(call $fd_write
|
||||
;; (i32.const 1) ;; fd: stdout = 1
|
||||
(i32.const 12) ;; data: pointer to memory - this is the first memory we create (index 0)
|
||||
(i32.const 1) ;; data_len: there's 1 string
|
||||
(i32.const 2468) ;; nwritten: i don't care about this, write it wherever
|
||||
)
|
||||
drop ;; drop number of bytes written
|
||||
)
|
||||
)
|
|
@ -17,7 +17,7 @@ use crate::Error;
|
|||
|
||||
/// pipeline to convert parsed tree -> wat/wasm
|
||||
pub struct Compiler<'a> {
|
||||
output: Box<&'a mut dyn std::io::Write>,
|
||||
output: &'a mut dyn std::io::Write,
|
||||
}
|
||||
|
||||
struct Allocator {
|
||||
|
@ -44,10 +44,8 @@ struct Allocator {
|
|||
// "#;
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
pub fn new(output: Box<&mut dyn Write>, _output_format: OutputFormat) -> Compiler {
|
||||
Compiler {
|
||||
output,
|
||||
}
|
||||
pub fn new(output: &mut dyn Write, _output_format: OutputFormat) -> Compiler {
|
||||
Compiler { output }
|
||||
}
|
||||
|
||||
pub fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> {
|
||||
|
@ -60,7 +58,12 @@ impl<'a> Compiler<'a> {
|
|||
|
||||
let mut gctx = GenContext::new();
|
||||
let mut alloc = Allocator::new();
|
||||
get_locals(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut alloc, &mut ctx, &mut gctx);
|
||||
get_locals(
|
||||
&Expr::Block(crate::data::Block(stmts.to_vec())),
|
||||
&mut alloc,
|
||||
&mut ctx,
|
||||
&mut gctx,
|
||||
);
|
||||
// get_strings(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut gctx);
|
||||
// if !gctx.strings.is_empty() {
|
||||
// writeln!(self.output, "(memory 1)")?;
|
||||
|
@ -81,7 +84,12 @@ impl<'a> Compiler<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_func(&mut self, parent_gctx: &GenContext, ctx: &Context, func: &Func) -> Result<(), Error> {
|
||||
fn write_func(
|
||||
&mut self,
|
||||
parent_gctx: &GenContext,
|
||||
ctx: &Context,
|
||||
func: &Func,
|
||||
) -> Result<(), Error> {
|
||||
write!(self.output, "(func ${}", func.name)?;
|
||||
|
||||
if func.name == "main" {
|
||||
|
@ -139,12 +147,19 @@ impl<'a> Compiler<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_expr(&mut self, expr: &Expr, parent_gctx: &GenContext, ctx: &Context) -> Result<(), Error> {
|
||||
fn gen_expr(
|
||||
&mut self,
|
||||
expr: &Expr,
|
||||
parent_gctx: &GenContext,
|
||||
ctx: &Context,
|
||||
) -> Result<(), Error> {
|
||||
match expr {
|
||||
Expr::Literal(lit) => match lit {
|
||||
Literal::Integer(int) => writeln!(self.output, "i32.const {int}")?,
|
||||
Literal::Float(f) => writeln!(self.output, "f64.const {f}")?,
|
||||
Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?,
|
||||
Literal::Boolean(b) => {
|
||||
writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?
|
||||
}
|
||||
// Literal::String(s) => writeln!(self.output, "i32.const {}", parent_gctx.strings.iter().find_map(|(s2, off)| (s == s2).then_some(off)).unwrap())?,
|
||||
_ => todo!(),
|
||||
},
|
||||
|
@ -210,8 +225,12 @@ impl<'a> Compiler<'a> {
|
|||
match pat {
|
||||
Pattern::Literal(lit) => {
|
||||
match lit {
|
||||
Literal::Integer(int) => writeln!(self.output, "i32.const {}", int)?,
|
||||
Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?,
|
||||
Literal::Integer(int) => {
|
||||
writeln!(self.output, "i32.const {}", int)?
|
||||
}
|
||||
Literal::Boolean(b) => {
|
||||
writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
writeln!(self.output, "local.get $match")?;
|
||||
|
@ -271,9 +290,7 @@ impl<'a> Compiler<'a> {
|
|||
// FIXME: not sure what i was thinking
|
||||
impl Allocator {
|
||||
fn new() -> Allocator {
|
||||
Allocator {
|
||||
count: 0,
|
||||
}
|
||||
Allocator { count: 0 }
|
||||
}
|
||||
|
||||
fn alloc_ident(&mut self, name: &str) -> String {
|
||||
|
@ -313,8 +330,11 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
|
|||
}
|
||||
Statement::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx),
|
||||
Statement::Expr(expr) => get_locals(expr, alloc, ctx, gctx),
|
||||
Statement::Func(Func { name, ret, params, .. }) => {
|
||||
ctx.funcs.insert(name.clone(), (params.clone(), ret.clone()));
|
||||
Statement::Func(Func {
|
||||
name, ret, params, ..
|
||||
}) => {
|
||||
ctx.funcs
|
||||
.insert(name.clone(), (params.clone(), ret.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,7 +345,11 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
|
|||
get_locals(b, alloc, ctx, gctx);
|
||||
}
|
||||
Expr::Literal(Literal::String(s)) => {
|
||||
let offset = gctx.strings.last().map(|(s, off)| off + s.len()).unwrap_or(8);
|
||||
let offset = gctx
|
||||
.strings
|
||||
.last()
|
||||
.map(|(s, off)| off + s.len())
|
||||
.unwrap_or(8);
|
||||
gctx.strings.push((s.clone(), offset));
|
||||
}
|
||||
Expr::Match(pat, arms) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// TODO: TypedStatement and TypedExpression?
|
||||
|
||||
use crate::lexer::{TokenContent, Symbol};
|
||||
use crate::lexer::{Symbol, TokenContent};
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -175,7 +175,7 @@ impl Type {
|
|||
Self::Tuple(v) => {
|
||||
let s: Vec<_> = v.iter().map(Type::string).collect();
|
||||
format!("({})", s.join(", "))
|
||||
},
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use miette::{Diagnostic, SourceSpan, NamedSource};
|
||||
use miette::{Diagnostic, NamedSource, SourceSpan};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
|
@ -40,7 +40,6 @@ pub struct TypeWrapper {
|
|||
pub struct SyntaxError {
|
||||
// #[source_code]
|
||||
// pub src: NamedSource,
|
||||
|
||||
#[help]
|
||||
pub help: String,
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::Error;
|
|||
|
||||
/// helper pipeline to convert wat/wasm -> actual text/binary
|
||||
pub struct Generator<'a> {
|
||||
output: Box<&'a mut dyn std::io::Write>,
|
||||
output: &'a mut dyn std::io::Write,
|
||||
format: OutputFormat,
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,8 @@ impl OutputFormat {
|
|||
}
|
||||
|
||||
impl Generator<'_> {
|
||||
pub fn new(output: Box<&mut dyn std::io::Write>, format: OutputFormat) -> Generator {
|
||||
Generator {
|
||||
output,
|
||||
format,
|
||||
}
|
||||
pub fn new(output: &mut dyn std::io::Write, format: OutputFormat) -> Generator {
|
||||
Generator { output, format }
|
||||
}
|
||||
|
||||
pub fn write_module(&mut self) -> Result<(), Error> {
|
||||
|
|
59
src/lexer.rs
59
src/lexer.rs
|
@ -4,8 +4,6 @@ use crate::Error;
|
|||
pub struct Lexer {
|
||||
input: Vec<char>,
|
||||
pos: usize,
|
||||
name: String,
|
||||
src: String,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
@ -51,12 +49,10 @@ pub enum Symbol {
|
|||
}
|
||||
|
||||
impl Lexer {
|
||||
pub fn new(input: &str, name: &str) -> Lexer {
|
||||
pub fn new(input: &str) -> Lexer {
|
||||
Lexer {
|
||||
input: input.chars().collect(),
|
||||
pos: 0,
|
||||
src: input.to_string(),
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +69,10 @@ impl Lexer {
|
|||
.get(self.pos)
|
||||
.is_some_and(|c| c.is_ascii_alphanumeric())
|
||||
{
|
||||
return Err(Error::syn((self.pos, 1), "there shouldn't be extra characters after the number"));
|
||||
return Err(Error::syn(
|
||||
(self.pos, 1),
|
||||
"there shouldn't be extra characters after the number",
|
||||
));
|
||||
}
|
||||
token
|
||||
}
|
||||
|
@ -81,7 +80,10 @@ impl Lexer {
|
|||
self.pos += 1;
|
||||
let ch = self.lex_char()?;
|
||||
if !self.input.get(self.pos).is_some_and(|c| *c == '\'') {
|
||||
return Err(Error::syn((start, self.pos - start), "close your character with a '"));
|
||||
return Err(Error::syn(
|
||||
(start, self.pos - start),
|
||||
"close your character with a '",
|
||||
));
|
||||
}
|
||||
self.pos += 1;
|
||||
TokenContent::Char(ch)
|
||||
|
@ -111,7 +113,10 @@ impl Lexer {
|
|||
}
|
||||
_ => self.lex_op()?,
|
||||
};
|
||||
Ok(Some(Token { token: tok, span: (start, self.pos - start) }))
|
||||
Ok(Some(Token {
|
||||
token: tok,
|
||||
span: (start, self.pos - start),
|
||||
}))
|
||||
}
|
||||
|
||||
fn lex_number(&mut self) -> Result<TokenContent, Error> {
|
||||
|
@ -144,7 +149,10 @@ impl Lexer {
|
|||
buffer.push(ch);
|
||||
self.pos += 1;
|
||||
} else if ch.is_ascii_digit() {
|
||||
return Err(Error::syn((self.pos, 1), "you seem to have used the wrong base for this number"));
|
||||
return Err(Error::syn(
|
||||
(self.pos, 1),
|
||||
"you seem to have used the wrong base for this number",
|
||||
));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -182,10 +190,12 @@ impl Lexer {
|
|||
let ch = match self.input.get(self.pos) {
|
||||
Some('\\') => {
|
||||
self.pos += 1;
|
||||
let ch = self
|
||||
.input
|
||||
.get(self.pos)
|
||||
.ok_or_else(|| Error::syn((self.pos, 1), "there should be an character here (which will be escaped)"))?;
|
||||
let ch = self.input.get(self.pos).ok_or_else(|| {
|
||||
Error::syn(
|
||||
(self.pos, 1),
|
||||
"there should be an character here (which will be escaped)",
|
||||
)
|
||||
})?;
|
||||
match ch {
|
||||
'n' => '\n',
|
||||
't' => '\t',
|
||||
|
@ -193,11 +203,23 @@ impl Lexer {
|
|||
'"' => '\"',
|
||||
// 'x' => '\x',
|
||||
// 'u' => '\u',
|
||||
_ => return Err(Error::syn((self.pos - 1, 2), &format!("i only know how to escape \\n, \\t, \\\\, or \\\", not \\{ch}"))),
|
||||
_ => {
|
||||
return Err(Error::syn(
|
||||
(self.pos - 1, 2),
|
||||
format!(
|
||||
"i only know how to escape \\n, \\t, \\\\, or \\\", not \\{ch}"
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ch) => *ch,
|
||||
None => return Err(Error::syn((self.pos, 1), "there should be a character here, not an eof")),
|
||||
None => {
|
||||
return Err(Error::syn(
|
||||
(self.pos, 1),
|
||||
"there should be a character here, not an eof",
|
||||
))
|
||||
}
|
||||
};
|
||||
self.pos += 1;
|
||||
Ok(ch)
|
||||
|
@ -360,7 +382,12 @@ impl Lexer {
|
|||
',' => Symbol::Comma,
|
||||
';' => Symbol::Semicolon,
|
||||
'?' => Symbol::Question,
|
||||
_ => return Err(Error::syn((self.pos, self.pos), "unexpected character here")),
|
||||
_ => {
|
||||
return Err(Error::syn(
|
||||
(self.pos, self.pos),
|
||||
"unexpected character here",
|
||||
))
|
||||
}
|
||||
};
|
||||
self.pos += 1;
|
||||
Ok(TokenContent::Symbol(symbol))
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(dead_code, clippy::single_match, clippy::only_used_in_recursion)]
|
||||
|
||||
pub mod compiler;
|
||||
pub mod data;
|
||||
pub mod error;
|
||||
pub mod compiler;
|
||||
pub mod generator;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
|
|
83
src/main.rs
83
src/main.rs
|
@ -4,10 +4,15 @@ a second time when generating (so the types are known), there should be
|
|||
a better way
|
||||
*/
|
||||
|
||||
use std::{path::PathBuf, fs::OpenOptions};
|
||||
use std::{fs::OpenOptions, path::PathBuf};
|
||||
|
||||
use clap::Parser;
|
||||
use lang::{compiler::Compiler, generator::OutputFormat, lexer, parser, error::{SyntaxWrapper, TypeWrapper}};
|
||||
use lang::{
|
||||
compiler::Compiler,
|
||||
error::{SyntaxWrapper, TypeWrapper},
|
||||
generator::OutputFormat,
|
||||
lexer, parser,
|
||||
};
|
||||
use miette::NamedSource;
|
||||
|
||||
#[derive(Debug, clap::Parser)]
|
||||
|
@ -31,7 +36,6 @@ enum Cli {
|
|||
/// The file containing the code you want to compile
|
||||
file: PathBuf,
|
||||
},
|
||||
|
||||
// future work:
|
||||
//
|
||||
// /// Start a read-print-eval loop
|
||||
|
@ -59,7 +63,8 @@ enum Cli {
|
|||
|
||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||
enum OutputType {
|
||||
Wat, Wasm,
|
||||
Wat,
|
||||
Wasm,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -93,10 +98,10 @@ fn main() {
|
|||
};
|
||||
let reporter = miette::GraphicalReportHandler::new().with_theme(theme);
|
||||
|
||||
match args {
|
||||
Cli::Run { file } => {
|
||||
let source = std::fs::read_to_string(&file).expect("no source!");
|
||||
let lexer = lexer::Lexer::new(&source, file.to_str().unwrap());
|
||||
match &args {
|
||||
Cli::Run { file } | Cli::Compile { file, .. } => {
|
||||
let source = std::fs::read_to_string(file).expect("no source!");
|
||||
let lexer = lexer::Lexer::new(&source);
|
||||
|
||||
let tokens = match lexer.collect::<Result<Vec<_>, _>>() {
|
||||
Ok(tokens) => tokens,
|
||||
|
@ -105,7 +110,7 @@ fn main() {
|
|||
lang::Error::SyntaxError(syn) => {
|
||||
let mut s = String::new();
|
||||
let syn = SyntaxWrapper {
|
||||
src: NamedSource::new(file.to_string_lossy().to_string(), source),
|
||||
src: NamedSource::new(file.to_string_lossy(), source),
|
||||
syn: vec![syn],
|
||||
};
|
||||
|
||||
|
@ -118,10 +123,10 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
let mut parser = parser::Parser::new(tokens, source.clone(), file.to_string_lossy().to_string());
|
||||
let mut parser = parser::Parser::new(tokens);
|
||||
let mut statements = vec![];
|
||||
loop {
|
||||
match parser.next() {
|
||||
match parser.parse_statement_option() {
|
||||
Ok(None) => break,
|
||||
Ok(Some(tree)) => statements.push(tree),
|
||||
Err(error) => {
|
||||
|
@ -129,7 +134,7 @@ fn main() {
|
|||
lang::Error::SyntaxError(syn) => {
|
||||
let mut s = String::new();
|
||||
let syn = SyntaxWrapper {
|
||||
src: NamedSource::new(file.to_string_lossy().to_string(), source),
|
||||
src: NamedSource::new(file.to_string_lossy(), source),
|
||||
syn: vec![syn],
|
||||
};
|
||||
reporter.render_report(&mut s, &syn).unwrap();
|
||||
|
@ -138,7 +143,7 @@ fn main() {
|
|||
lang::Error::TypeError(ty) => {
|
||||
let mut s = String::new();
|
||||
let ty = TypeWrapper {
|
||||
src: NamedSource::new(file.to_string_lossy().to_string(), source),
|
||||
src: NamedSource::new(file.to_string_lossy(), source),
|
||||
ty: vec![ty],
|
||||
};
|
||||
reporter.render_report(&mut s, &ty).unwrap();
|
||||
|
@ -151,8 +156,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
match args {
|
||||
Cli::Run { .. } => {
|
||||
let mut wat = vec![];
|
||||
let mut gen = Compiler::new(Box::new(&mut wat), OutputFormat::WatVerbose);
|
||||
let mut gen = Compiler::new(&mut wat, OutputFormat::WatVerbose);
|
||||
|
||||
if let Err(err) = gen.write_module(&statements) {
|
||||
panic!("{:?}", err);
|
||||
|
@ -164,34 +171,16 @@ fn main() {
|
|||
let linker = wasmtime::Linker::new(&engine);
|
||||
let mut store = wasmtime::Store::new(&engine, 4);
|
||||
let instance = linker.instantiate(&mut store, &module).unwrap();
|
||||
let start = instance.get_typed_func::<(), i32>(&mut store, "_start").unwrap();
|
||||
let start = instance
|
||||
.get_typed_func::<(), i32>(&mut store, "_start")
|
||||
.unwrap();
|
||||
dbg!(start.call(&mut store, ()).unwrap());
|
||||
}
|
||||
Cli::Compile { file, format, output } => {
|
||||
let source = std::fs::read_to_string(&file).expect("no source!");
|
||||
let lexer = lexer::Lexer::new(&source, file.to_str().unwrap());
|
||||
|
||||
let tokens = match lexer.collect::<Result<Vec<_>, _>>() {
|
||||
Ok(tokens) => tokens,
|
||||
Err(error) => {
|
||||
eprintln!("error: {:?}", error);
|
||||
return
|
||||
}
|
||||
};
|
||||
|
||||
let mut parser = parser::Parser::new(tokens, source.clone(), file.to_string_lossy().to_string());
|
||||
let mut statements = vec![];
|
||||
loop {
|
||||
match parser.next() {
|
||||
Ok(None) => break,
|
||||
Ok(Some(tree)) => statements.push(tree),
|
||||
Err(error) => {
|
||||
eprintln!("error: {:?}", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cli::Compile {
|
||||
format,
|
||||
output,
|
||||
file,
|
||||
} => {
|
||||
let format = format.unwrap_or_else(|| {
|
||||
if file.extension().is_some_and(|ext| ext == "wasm") {
|
||||
OutputType::Wasm
|
||||
|
@ -201,20 +190,26 @@ fn main() {
|
|||
});
|
||||
|
||||
let mut output: Box<dyn std::io::Write> = match output {
|
||||
Some(path) => Box::new(OpenOptions::new().create(true).write(true).open(path).expect("failed to open file")),
|
||||
Some(path) => Box::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.expect("failed to open file"),
|
||||
),
|
||||
None => Box::new(std::io::stdout()),
|
||||
};
|
||||
|
||||
match format {
|
||||
OutputType::Wat => {
|
||||
let mut gen = Compiler::new(Box::new(&mut output), OutputFormat::WatVerbose);
|
||||
let mut gen = Compiler::new(&mut output, OutputFormat::WatVerbose);
|
||||
|
||||
if let Err(err) = gen.write_module(&statements) {
|
||||
panic!("{:?}", err);
|
||||
}
|
||||
}
|
||||
OutputType::Wasm => {
|
||||
let mut gen = Compiler::new(Box::new(&mut output), OutputFormat::Wasm);
|
||||
let mut gen = Compiler::new(&mut output, OutputFormat::Wasm);
|
||||
|
||||
if let Err(err) = gen.write_module(&statements) {
|
||||
panic!("{:?}", err);
|
||||
|
@ -224,3 +219,5 @@ fn main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
112
src/parser.rs
112
src/parser.rs
|
@ -1,14 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::data::{BinaryOp, Block, Expr, Literal, Pattern, PrefixOp, Statement, Type, Func};
|
||||
use crate::lexer::{TokenContent, Symbol, Token};
|
||||
use crate::data::{BinaryOp, Block, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type};
|
||||
use crate::lexer::{Symbol, Token, TokenContent};
|
||||
use crate::Error;
|
||||
|
||||
pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
pos: usize,
|
||||
src: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -18,13 +16,8 @@ pub struct Context {
|
|||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<Token>, src: String, name: String) -> Parser {
|
||||
Parser {
|
||||
src,
|
||||
name,
|
||||
tokens,
|
||||
pos: 0,
|
||||
}
|
||||
pub fn new(tokens: Vec<Token>) -> Parser {
|
||||
Parser { tokens, pos: 0 }
|
||||
}
|
||||
|
||||
fn err_syn(&self, pos: (usize, usize), help: &str) -> Error {
|
||||
|
@ -32,7 +25,10 @@ impl Parser {
|
|||
}
|
||||
|
||||
fn err_eof(&self, wanted: &str) -> Error {
|
||||
self.err_syn(self.tokens.last().map(|tok| tok.span).unwrap_or((0, 0)), &format!("wanted {}, got eof", wanted))
|
||||
self.err_syn(
|
||||
self.tokens.last().map(|tok| tok.span).unwrap_or((0, 0)),
|
||||
&format!("wanted {}, got eof", wanted),
|
||||
)
|
||||
}
|
||||
|
||||
fn err_ty_unknown(&self, pos: (usize, usize), help: &str) -> Error {
|
||||
|
@ -65,7 +61,7 @@ impl Parser {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Result<Option<Statement>, Error> {
|
||||
pub fn parse_statement_option(&mut self) -> Result<Option<Statement>, Error> {
|
||||
match self.peek_tok() {
|
||||
Some(_) => Some(self.parse_statement()).transpose(),
|
||||
None => Ok(None),
|
||||
|
@ -80,7 +76,7 @@ impl Parser {
|
|||
TokenContent::Let => {
|
||||
self.eat(TokenContent::Let)?;
|
||||
let name = self.parse_ident()?;
|
||||
if let Ok(_) = self.eat(TokenContent::Symbol(Symbol::Colon)) {
|
||||
if self.eat(TokenContent::Symbol(Symbol::Colon)).is_ok() {
|
||||
// TODO: types in variables
|
||||
self.parse_type()?;
|
||||
}
|
||||
|
@ -101,7 +97,10 @@ impl Parser {
|
|||
let name = self.parse_ident()?;
|
||||
self.eat(TokenContent::OpenParan)?;
|
||||
let mut params = vec![];
|
||||
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) {
|
||||
while !self
|
||||
.peek_tok()
|
||||
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
|
||||
{
|
||||
let name = self.parse_ident()?;
|
||||
self.eat(TokenContent::Symbol(Symbol::Colon))?;
|
||||
let ty = self.parse_type()?;
|
||||
|
@ -121,11 +120,20 @@ impl Parser {
|
|||
self.eat(TokenContent::OpenBrace)?;
|
||||
let block = self.parse_block()?;
|
||||
self.eat(TokenContent::CloseBrace)?;
|
||||
Statement::Func(Func { name, params, ret, block, public })
|
||||
Statement::Func(Func {
|
||||
name,
|
||||
params,
|
||||
ret,
|
||||
block,
|
||||
public,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let expr = self.parse_expr(0)?;
|
||||
if self.peek_tok().is_some_and(|tk| tk.token == TokenContent::Symbol(Symbol::Semicolon)) {
|
||||
if self
|
||||
.peek_tok()
|
||||
.is_some_and(|tk| tk.token == TokenContent::Symbol(Symbol::Semicolon))
|
||||
{
|
||||
self.eat(TokenContent::Symbol(Symbol::Semicolon))?;
|
||||
Statement::Expr(expr)
|
||||
} else {
|
||||
|
@ -145,11 +153,19 @@ impl Parser {
|
|||
"i32" => Type::Integer,
|
||||
"f64" => Type::Float,
|
||||
"bool" => Type::Boolean,
|
||||
_ => return Err(self.err_ty_unknown(tok.span, "use a type that i know (currently only i32 and bool)")),
|
||||
_ => {
|
||||
return Err(self.err_ty_unknown(
|
||||
tok.span,
|
||||
"use a type that i know (currently only i32 and bool)",
|
||||
))
|
||||
}
|
||||
},
|
||||
TokenContent::OpenParan => {
|
||||
let mut tys = vec![];
|
||||
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) {
|
||||
while !self
|
||||
.peek_tok()
|
||||
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
|
||||
{
|
||||
tys.push(self.parse_type()?);
|
||||
|
||||
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
|
||||
|
@ -158,7 +174,7 @@ impl Parser {
|
|||
}
|
||||
self.eat(TokenContent::CloseParan)?;
|
||||
Type::Tuple(tys)
|
||||
},
|
||||
}
|
||||
_ => return Err(self.err_syn(tok.span, "this should be a pattern")),
|
||||
};
|
||||
Ok(ty)
|
||||
|
@ -166,8 +182,13 @@ impl Parser {
|
|||
|
||||
fn parse_ident(&mut self) -> Result<String, Error> {
|
||||
let result = match self.peek_tok() {
|
||||
Some(Token { token: TokenContent::Ident(ident), .. }) => Ok(ident.to_string()),
|
||||
Some(t) => return Err(self.err_syn(t.span, &format!("expected ident, got {:?}", t.token))),
|
||||
Some(Token {
|
||||
token: TokenContent::Ident(ident),
|
||||
..
|
||||
}) => Ok(ident.to_string()),
|
||||
Some(t) => {
|
||||
return Err(self.err_syn(t.span, &format!("expected ident, got {:?}", t.token)))
|
||||
}
|
||||
None => return Err(self.err_eof("ident")),
|
||||
};
|
||||
self.pos += 1;
|
||||
|
@ -177,7 +198,10 @@ impl Parser {
|
|||
fn parse_block(&mut self) -> Result<Block, Error> {
|
||||
let mut statements = vec![];
|
||||
loop {
|
||||
if self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseBrace) {
|
||||
if self
|
||||
.peek_tok()
|
||||
.is_some_and(|tok| tok.token == TokenContent::CloseBrace)
|
||||
{
|
||||
break;
|
||||
}
|
||||
match self.parse_statement()? {
|
||||
|
@ -188,7 +212,10 @@ impl Parser {
|
|||
stmt => statements.push(stmt),
|
||||
}
|
||||
match self.peek_tok() {
|
||||
Some(Token { token: TokenContent::CloseBrace, .. }) => break,
|
||||
Some(Token {
|
||||
token: TokenContent::CloseBrace,
|
||||
..
|
||||
}) => break,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
@ -212,7 +239,10 @@ impl Parser {
|
|||
let ident = ident.clone();
|
||||
if self.eat(TokenContent::OpenParan).is_ok() {
|
||||
let mut params = vec![];
|
||||
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) {
|
||||
while !self
|
||||
.peek_tok()
|
||||
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
|
||||
{
|
||||
params.push(self.parse_expr(0)?);
|
||||
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
|
||||
break;
|
||||
|
@ -233,17 +263,30 @@ impl Parser {
|
|||
self.eat(TokenContent::OpenBrace)?;
|
||||
let block = self.parse_block()?;
|
||||
self.eat(TokenContent::CloseBrace)?;
|
||||
let otherwise = if self.peek_tok().is_some_and(|t| t.token == TokenContent::Else) {
|
||||
let otherwise = if self
|
||||
.peek_tok()
|
||||
.is_some_and(|t| t.token == TokenContent::Else)
|
||||
{
|
||||
self.next_tok();
|
||||
match self.peek_tok() {
|
||||
Some(Token { token: TokenContent::OpenBrace, .. }) => {
|
||||
Some(Token {
|
||||
token: TokenContent::OpenBrace,
|
||||
..
|
||||
}) => {
|
||||
self.eat(TokenContent::OpenBrace)?;
|
||||
let b = Some(self.parse_block()?);
|
||||
self.eat(TokenContent::CloseBrace)?;
|
||||
b
|
||||
}
|
||||
Some(Token { token: TokenContent::If, .. }) => Some(Block(vec![Statement::TailExpr(self.parse_expr(0)?)])),
|
||||
Some(tk) => return Err(self.err_syn(tk.span, "this should be followed by an if or block")),
|
||||
Some(Token {
|
||||
token: TokenContent::If,
|
||||
..
|
||||
}) => Some(Block(vec![Statement::TailExpr(self.parse_expr(0)?)])),
|
||||
Some(tk) => {
|
||||
return Err(
|
||||
self.err_syn(tk.span, "this should be followed by an if or block")
|
||||
)
|
||||
}
|
||||
None => return Err(self.err_eof("if or block")),
|
||||
}
|
||||
} else {
|
||||
|
@ -260,7 +303,10 @@ impl Parser {
|
|||
}
|
||||
TokenContent::Symbol(_) => {
|
||||
let Some(op) = PrefixOp::from_token(&tok.token) else {
|
||||
return Err(self.err_syn(tok.span, "this should be changed into a valid operator or removed outright"));
|
||||
return Err(self.err_syn(
|
||||
tok.span,
|
||||
"this should be changed into a valid operator or removed outright",
|
||||
));
|
||||
};
|
||||
let expr = self.parse_expr(1)?;
|
||||
Expr::Unary(op, Box::new(expr))
|
||||
|
@ -279,7 +325,11 @@ impl Parser {
|
|||
break;
|
||||
}
|
||||
|
||||
if self.peek_tok().is_none() || self.peek_tok().is_some_and(|t| t.token == TokenContent::CloseBrace) {
|
||||
if self.peek_tok().is_none()
|
||||
|| self
|
||||
.peek_tok()
|
||||
.is_some_and(|t| t.token == TokenContent::CloseBrace)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
2
test/bad
2
test/bad
|
@ -1,3 +1,3 @@
|
|||
fn main() {
|
||||
let a = 10 * true;
|
||||
let a = 10 +
|
||||
}
|
|
@ -1,14 +1,44 @@
|
|||
use lang::{lexer::{Lexer, TokenContent, Symbol}, Error};
|
||||
use lang::{
|
||||
lexer::{Lexer, Symbol, Token, TokenContent},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_foo() {
|
||||
let tokens: Result<Vec<_>, Error> = Lexer::new("1 * 5 / 3").collect();
|
||||
let tokens = tokens.expect("should parse");
|
||||
assert_eq!(tokens, vec![
|
||||
TokenContent::Number { radix: 10, text: "1".into() },
|
||||
TokenContent::Symbol(Symbol::Star),
|
||||
TokenContent::Number { radix: 10, text: "5".into() },
|
||||
TokenContent::Symbol(Symbol::Slash),
|
||||
TokenContent::Number { radix: 10, text: "3".into() },
|
||||
])
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![
|
||||
Token {
|
||||
span: (0, 1),
|
||||
token: TokenContent::Number {
|
||||
radix: 10,
|
||||
text: "1".into()
|
||||
},
|
||||
},
|
||||
Token {
|
||||
span: (1, 1),
|
||||
token: TokenContent::Symbol(Symbol::Star),
|
||||
},
|
||||
Token {
|
||||
span: (2, 1),
|
||||
token: TokenContent::Number {
|
||||
radix: 10,
|
||||
text: "5".into()
|
||||
},
|
||||
},
|
||||
Token {
|
||||
span: (3, 1),
|
||||
token: TokenContent::Symbol(Symbol::Slash),
|
||||
},
|
||||
Token {
|
||||
span: (4, 1),
|
||||
token: TokenContent::Number {
|
||||
radix: 10,
|
||||
text: "3".into()
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue