Add basic parsing of let statements
This commit is contained in:
parent
1b28558dd8
commit
bea027d730
5
Cargo.lock
generated
Normal file
5
Cargo.lock
generated
Normal file
@ -0,0 +1,5 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "Reid"
|
||||
version = "0.1.0"
|
1
reid_src/test.reid
Normal file
1
reid_src/test.reid
Normal file
@ -0,0 +1 @@
|
||||
let otus = "gotus";
|
24
src/errors.rs
Normal file
24
src/errors.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use super::parser::Position;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GenericError {
|
||||
StdIOError(io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for GenericError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::StdIOError(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompilerError {
|
||||
Fatal,
|
||||
PeekFailed,
|
||||
ExpectedToken(Position, char),
|
||||
ExpectedExpression(Position, Box<CompilerError>),
|
||||
ExpectedIdent(Position),
|
||||
ExpectedStatement(Position),
|
||||
ExpectedPattern(Position),
|
||||
}
|
14
src/file_io.rs
Normal file
14
src/file_io.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
use super::errors::GenericError;
|
||||
|
||||
pub fn open_file(path: &Path) -> Result<String, GenericError> {
|
||||
let file = File::open(path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut text = String::new();
|
||||
reader.read_to_string(&mut text)?;
|
||||
Ok(text)
|
||||
}
|
12
src/main.rs
12
src/main.rs
@ -1,3 +1,13 @@
|
||||
mod errors;
|
||||
mod file_io;
|
||||
mod parser;
|
||||
|
||||
use file_io::open_file;
|
||||
use parser::{ParsedReid, Parser};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let path = Path::new("reid_src/test.reid");
|
||||
let reid = Parser::from(open_file(&path).ok().unwrap()).parse();
|
||||
println!("Parsed: {:?}", reid);
|
||||
}
|
||||
|
327
src/parser.rs
Normal file
327
src/parser.rs
Normal file
@ -0,0 +1,327 @@
|
||||
use super::errors::CompilerError;
|
||||
|
||||
type Ident = String;
|
||||
|
||||
const ALLOWED_IDENT_CHARS: [char; 38] = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
|
||||
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '-',
|
||||
];
|
||||
const ALLOWED_IDENT_BEGIN_CHARS: [char; 26] = [
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
|
||||
't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
];
|
||||
|
||||
pub struct Parser {
|
||||
text: Vec<char>,
|
||||
cursor: usize,
|
||||
line_number: usize,
|
||||
character_number: usize,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn from(text: String) -> Self {
|
||||
Parser {
|
||||
text: text.chars().collect(),
|
||||
cursor: 0,
|
||||
line_number: 0,
|
||||
character_number: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(mut self) -> Result<ParsedReid, CompilerError> {
|
||||
let mut exp_list = Vec::new();
|
||||
|
||||
let mut error = None;
|
||||
|
||||
while {
|
||||
self.skip_whitespace();
|
||||
if self.remaining() > 0 {
|
||||
match Expression::parse(&mut self) {
|
||||
Ok(exp) => {
|
||||
exp_list.push(exp);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
error = Some(err);
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} {}
|
||||
|
||||
if let Some(error) = error {
|
||||
Err(error)
|
||||
} else {
|
||||
Ok(ParsedReid(Expression::BlockExpr(Position(0, 0), exp_list)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remaining(&self) -> usize {
|
||||
self.text.len() - self.cursor
|
||||
}
|
||||
|
||||
pub fn peek(&mut self, index: usize) -> Option<char> {
|
||||
if self.remaining() < (index + 1) {
|
||||
None
|
||||
} else {
|
||||
Some(self.text[self.cursor + index])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect<T: Into<String>>(&mut self, expected: T) -> Expect {
|
||||
let expected = expected.into();
|
||||
self.skip_whitespace();
|
||||
let mut result = Some(expected.clone());
|
||||
for (idx, c) in expected.chars().enumerate() {
|
||||
if let Some(peek) = self.peek(idx) {
|
||||
if peek != c {
|
||||
result = None;
|
||||
}
|
||||
} else {
|
||||
result = None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Expect {
|
||||
text: result,
|
||||
len: expected.len(),
|
||||
parser: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_ident(&mut self) -> Expect {
|
||||
self.skip_whitespace();
|
||||
let mut ident: Option<String> = None;
|
||||
let mut len = 0;
|
||||
while {
|
||||
if let Some(peek) = self.peek(len) {
|
||||
let lowercase = &peek.to_ascii_lowercase();
|
||||
if let Some(id) = &mut ident {
|
||||
if ALLOWED_IDENT_CHARS.contains(lowercase) {
|
||||
id.push(peek);
|
||||
len += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
if ALLOWED_IDENT_BEGIN_CHARS.contains(lowercase) {
|
||||
ident = Some(peek.to_string());
|
||||
len += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} {}
|
||||
Expect {
|
||||
text: ident,
|
||||
len: len,
|
||||
parser: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_string_lit(&mut self) -> Expect {
|
||||
self.skip_whitespace();
|
||||
let mut content: Option<String> = None;
|
||||
let mut len = 0;
|
||||
while {
|
||||
if let Some(peek) = self.peek(len) {
|
||||
if let Some(cont) = &mut content {
|
||||
if peek == '"' {
|
||||
len += 1;
|
||||
false
|
||||
} else {
|
||||
cont.push(peek);
|
||||
len += 1;
|
||||
true
|
||||
}
|
||||
} else {
|
||||
if peek == '"' {
|
||||
content = Some(String::new());
|
||||
len += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} {}
|
||||
Expect {
|
||||
text: content,
|
||||
len: len,
|
||||
parser: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_cursor(&mut self) {
|
||||
let curr = self.peek(0).unwrap();
|
||||
self.cursor += 1;
|
||||
self.character_number += 1;
|
||||
if curr == '\n' {
|
||||
self.character_number = 0;
|
||||
self.line_number += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_cursor_multiple(&mut self, amount: usize) {
|
||||
for _ in 0..amount {
|
||||
self.move_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> Position {
|
||||
Position(self.line_number, self.character_number)
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) {
|
||||
let mut next = ' ';
|
||||
while self.remaining() > 0 && {
|
||||
next = self.peek(0).unwrap();
|
||||
next == ' ' || next == '\n' || next == '\r' || next == '\t'
|
||||
} {
|
||||
self.move_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
fn empty() -> Parser {
|
||||
Parser {
|
||||
text: Vec::new(),
|
||||
cursor: 0,
|
||||
character_number: 0,
|
||||
line_number: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParsedReid(Expression);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Expression {
|
||||
BlockExpr(Position, Vec<Expression>),
|
||||
StatementExpr(Position, Statement),
|
||||
EmptyExpr,
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn parse(parser: &mut Parser) -> Result<Expression, CompilerError> {
|
||||
let begin_pos = parser.pos();
|
||||
|
||||
let expect = parser.expect("{");
|
||||
if let Some(_) = expect.get() {
|
||||
let mut exp_list = Vec::new();
|
||||
while {
|
||||
match Expression::parse(parser) {
|
||||
Ok(exp) => {
|
||||
exp_list.push(exp);
|
||||
true
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
} {}
|
||||
if let Some(_) = parser.expect("}").get() {
|
||||
Ok(Expression::BlockExpr(begin_pos, exp_list))
|
||||
} else {
|
||||
Err(CompilerError::ExpectedToken(parser.pos(), '}'))
|
||||
}
|
||||
} else {
|
||||
match Statement::parse(parser) {
|
||||
Ok(statement) => Ok(Expression::StatementExpr(begin_pos, statement)),
|
||||
Err(err) => Err(CompilerError::ExpectedExpression(begin_pos, Box::new(err))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Statement {
|
||||
LetStatement(Position, Ident, Box<Expression>),
|
||||
ValueRef(Position, Pattern),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn parse(parser: &mut Parser) -> Result<Statement, CompilerError> {
|
||||
let pos = parser.pos();
|
||||
|
||||
if let Some(_) = parser.expect("let").get() {
|
||||
let ident = parser
|
||||
.expect_ident()
|
||||
.get_or(CompilerError::ExpectedIdent(pos))?;
|
||||
parser
|
||||
.expect("=")
|
||||
.get_or(CompilerError::ExpectedToken(pos, '='))?;
|
||||
match Expression::parse(parser) {
|
||||
Ok(expr) => {
|
||||
parser
|
||||
.expect(";")
|
||||
.get_or(CompilerError::ExpectedToken(pos, ';'))?;
|
||||
Ok(Statement::LetStatement(pos, ident, Box::new(expr)))
|
||||
}
|
||||
Err(err) => Err(CompilerError::ExpectedExpression(pos, Box::new(err))),
|
||||
}
|
||||
} else if let Ok(pattern) = Pattern::parse(parser) {
|
||||
Ok(Statement::ValueRef(pos, pattern))
|
||||
} else {
|
||||
Err(CompilerError::ExpectedStatement(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Pattern {
|
||||
IdentPattern(Position, Ident),
|
||||
LiteralPattern(Position, LiteralPattern),
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
fn parse(parser: &mut Parser) -> Result<Pattern, CompilerError> {
|
||||
let pos = parser.pos();
|
||||
if let Some(string) = parser.expect_string_lit().get() {
|
||||
Ok(Pattern::LiteralPattern(
|
||||
pos,
|
||||
LiteralPattern::StringLit(string),
|
||||
))
|
||||
} else if let Some(ident) = parser.expect_ident().get() {
|
||||
Ok(Pattern::IdentPattern(pos, ident))
|
||||
} else {
|
||||
Err(CompilerError::ExpectedPattern(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LiteralPattern {
|
||||
StringLit(String),
|
||||
}
|
||||
|
||||
pub struct Expect<'a> {
|
||||
text: Option<String>,
|
||||
len: usize,
|
||||
parser: &'a mut Parser,
|
||||
}
|
||||
|
||||
impl Expect<'_> {
|
||||
pub fn get(mut self) -> Option<String> {
|
||||
if self.text.is_some() {
|
||||
self.parser.move_cursor_multiple(self.len);
|
||||
}
|
||||
self.text
|
||||
}
|
||||
pub fn get_or(mut self, error: CompilerError) -> Result<String, CompilerError> {
|
||||
match self.get() {
|
||||
Some(text) => Ok(text),
|
||||
None => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Position(usize, usize);
|
Loading…
Reference in New Issue
Block a user