mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-07 05:28:45 -04:00
first
This commit is contained in:
commit
7970cb197a
10 changed files with 595 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "automata"
|
||||||
|
version = "0.1.0"
|
||||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "automata"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
35
default.nix
Normal file
35
default.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.mkShell rec {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
clang
|
||||||
|
# Replace llvmPackages with llvmPackages_X, where X is the latest LLVM version (at the time of writing, 16)
|
||||||
|
llvmPackages.bintools
|
||||||
|
rustup
|
||||||
|
nasm
|
||||||
|
];
|
||||||
|
RUSTC_VERSION = "nightly";
|
||||||
|
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||||
|
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||||
|
shellHook = ''
|
||||||
|
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||||
|
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||||
|
'';
|
||||||
|
# Add precompiled library to rustc search path
|
||||||
|
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||||
|
# add libraries here (e.g. pkgs.libvmi)
|
||||||
|
]);
|
||||||
|
# Add glibc, clang, glib and other headers to bindgen search path
|
||||||
|
BINDGEN_EXTRA_CLANG_ARGS =
|
||||||
|
# Includes with normal include path
|
||||||
|
(builtins.map (a: ''-I"${a}/include"'') [
|
||||||
|
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||||
|
pkgs.glibc.dev
|
||||||
|
])
|
||||||
|
# Includes with special directory paths
|
||||||
|
++ [
|
||||||
|
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||||
|
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||||
|
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
17
example.txt
Normal file
17
example.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
Q = {q0, q1} // states
|
||||||
|
E = {a, b} // alphabet
|
||||||
|
T = {z0, A, B} // stack symbols
|
||||||
|
|
||||||
|
// construct all possible permutations of A's and B's
|
||||||
|
d(q0, epsilon, z0)={(q0, A z0), (q0, B z0)}
|
||||||
|
d(q0, epsilon, A)={(q0, A A), (q0, B A)}
|
||||||
|
d(q0, epsilon, B)={(q0, A B), (q0, B B)}
|
||||||
|
|
||||||
|
// transition to q1
|
||||||
|
d(q0, epsilon, z0)={(q1, z0)}
|
||||||
|
d(q0, epsilon, A)={(q1, A)}
|
||||||
|
d(q0, epsilon, B)={(q1, B)}
|
||||||
|
|
||||||
|
// consume stack until empty
|
||||||
|
d(q1, a, A)={(q1, epsilon)}
|
||||||
|
d(q1, b, B)={(q1, epsilon)}
|
||||||
12
src/dfa.rs
Normal file
12
src/dfa.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub struct TransitionTable {
|
||||||
|
initial: State,
|
||||||
|
state_names: Vec<String>,
|
||||||
|
transitions: Vec<SymbolMap<State>>,
|
||||||
|
final_states: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DFA {
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
215
src/lexer.rs
Normal file
215
src/lexer.rs
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Token<'a> {
|
||||||
|
LPar,
|
||||||
|
RPar,
|
||||||
|
|
||||||
|
LBrace,
|
||||||
|
RBrace,
|
||||||
|
|
||||||
|
LBracket,
|
||||||
|
RBracket,
|
||||||
|
|
||||||
|
Tilde,
|
||||||
|
Eq,
|
||||||
|
Comma,
|
||||||
|
|
||||||
|
Or,
|
||||||
|
Plus,
|
||||||
|
Star,
|
||||||
|
And,
|
||||||
|
|
||||||
|
LSmallArrow,
|
||||||
|
LBigArrow,
|
||||||
|
|
||||||
|
Comment(&'a str),
|
||||||
|
|
||||||
|
Ident(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Display for Token<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Token::LPar => write!(f, ")"),
|
||||||
|
Token::RPar => write!(f, "("),
|
||||||
|
Token::LBrace => write!(f, "{{"),
|
||||||
|
Token::RBrace => write!(f, "}}"),
|
||||||
|
Token::LBracket => write!(f, "["),
|
||||||
|
Token::RBracket => write!(f, "]"),
|
||||||
|
Token::Tilde => write!(f, "~"),
|
||||||
|
Token::Eq => write!(f, "="),
|
||||||
|
Token::Comma => write!(f, ","),
|
||||||
|
Token::Or => write!(f, "|"),
|
||||||
|
Token::Plus => write!(f, "+"),
|
||||||
|
Token::Star => write!(f, "*"),
|
||||||
|
Token::And => write!(f, "&"),
|
||||||
|
Token::LSmallArrow => write!(f, "->"),
|
||||||
|
Token::LBigArrow => write!(f, "=>"),
|
||||||
|
Token::Comment(_) => write!(f, "<comment>"),
|
||||||
|
Token::Ident(ident) if f.alternate() => write!(f, "{ident:?}"),
|
||||||
|
Token::Ident(_) => write!(f, "ident"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Span(pub usize, pub usize);
|
||||||
|
impl Span {
|
||||||
|
pub fn join(&self, end: Span) -> Span {
|
||||||
|
Span(self.0, end.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Spanned<T>(pub T, pub Span);
|
||||||
|
impl<T> Spanned<T> {
|
||||||
|
pub fn map<R>(self, map: impl Fn(T) -> R) -> Spanned<R> {
|
||||||
|
Spanned(map(self.0), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Lexer<'a> {
|
||||||
|
input: &'a str,
|
||||||
|
|
||||||
|
start: usize,
|
||||||
|
position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
InvalidChar(char),
|
||||||
|
UnclosedMultiLine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Lexer<'a> {
|
||||||
|
pub fn new(input: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
start: 0,
|
||||||
|
position: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self) -> Option<char> {
|
||||||
|
let next = self.input.get(self.position..)?.chars().next()?;
|
||||||
|
self.position += next.len_utf8();
|
||||||
|
Some(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Option<char> {
|
||||||
|
self.input.get(self.position..)?.chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backtrack(&mut self) {
|
||||||
|
if let Some(consumed) = self.input.get(..self.position)
|
||||||
|
&& let Some(previous) = consumed.chars().next_back()
|
||||||
|
{
|
||||||
|
self.position -= previous.len_utf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eof_span(&self) -> Span {
|
||||||
|
Span(self.input.len(), self.input.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::iter::Iterator for Lexer<'a> {
|
||||||
|
type Item = Spanned<Result<Token<'a>, Error>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(c) = self.peek()
|
||||||
|
&& c.is_whitespace()
|
||||||
|
{
|
||||||
|
self.consume();
|
||||||
|
}
|
||||||
|
self.start = self.position;
|
||||||
|
|
||||||
|
let res = match self.consume()? {
|
||||||
|
'(' => Ok(Token::LPar),
|
||||||
|
')' => Ok(Token::LPar),
|
||||||
|
'{' => Ok(Token::LBrace),
|
||||||
|
'}' => Ok(Token::RBrace),
|
||||||
|
'[' => Ok(Token::LBracket),
|
||||||
|
']' => Ok(Token::RBracket),
|
||||||
|
'~' => Ok(Token::Tilde),
|
||||||
|
'+' => Ok(Token::Plus),
|
||||||
|
'*' => Ok(Token::Star),
|
||||||
|
'&' => Ok(Token::And),
|
||||||
|
',' => Ok(Token::Comma),
|
||||||
|
'|' => Ok(Token::Or),
|
||||||
|
'=' => match self.peek() {
|
||||||
|
Some('>') => {
|
||||||
|
self.consume();
|
||||||
|
Ok(Token::LBigArrow)
|
||||||
|
}
|
||||||
|
_ => Ok(Token::Eq),
|
||||||
|
},
|
||||||
|
'-' => match self.peek() {
|
||||||
|
Some('>') => {
|
||||||
|
self.consume();
|
||||||
|
Ok(Token::LSmallArrow)
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidChar('-')),
|
||||||
|
},
|
||||||
|
|
||||||
|
'/' => match self.consume() {
|
||||||
|
Some('/') => loop {
|
||||||
|
if let Some('\n') | None = self.consume() {
|
||||||
|
break Ok(Token::Comment(&self.input[self.start + 2..self.position]));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some('*') => loop {
|
||||||
|
match self.consume() {
|
||||||
|
Some('*') if self.peek() == Some('/') => {
|
||||||
|
self.consume();
|
||||||
|
break Ok(Token::Comment(
|
||||||
|
&self.input[self.start + 2..self.position - 2],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
None => break Err(Error::UnclosedMultiLine),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(_) => {
|
||||||
|
self.backtrack();
|
||||||
|
Err(Error::InvalidChar('/'))
|
||||||
|
}
|
||||||
|
None => Err(Error::InvalidChar('/')),
|
||||||
|
},
|
||||||
|
|
||||||
|
c if c.is_alphabetic() || c == '_' => loop {
|
||||||
|
match self.consume() {
|
||||||
|
Some(c) if c.is_alphanumeric() || c == '_' => {}
|
||||||
|
Some(_) => {
|
||||||
|
self.backtrack();
|
||||||
|
break Ok(Token::Ident(&self.input[self.start..self.position]));
|
||||||
|
}
|
||||||
|
None => break Ok(Token::Ident(&self.input[self.start..self.position])),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
c => Err(Error::InvalidChar(c)),
|
||||||
|
};
|
||||||
|
let span = Span(self.start, self.position);
|
||||||
|
self.start = self.position;
|
||||||
|
Some(Spanned(res, span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tokenizer() {
|
||||||
|
let tests = [
|
||||||
|
"",
|
||||||
|
"/*",
|
||||||
|
"/**",
|
||||||
|
"/*/",
|
||||||
|
"/**/",
|
||||||
|
"/",
|
||||||
|
"//",
|
||||||
|
"()[]{}~=>==>->-+*&|, hello _th012is__ a wondweful",
|
||||||
|
];
|
||||||
|
|
||||||
|
for test in tests {
|
||||||
|
println!("'{test}': {:?}", Lexer::new(test).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/lib.rs
Normal file
11
src/lib.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
pub mod dfa;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
|
|
||||||
|
pub struct SymbolMap<T>([T; 256]);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub struct State(u16);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Symbol(u16);
|
||||||
7
src/main.rs
Normal file
7
src/main.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
use automata::{lexer::Lexer, parser::Parser};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = include_str!("../example.txt");
|
||||||
|
|
||||||
|
println!("{:#?}", Parser::new(Lexer::new(input)).parse_elements());
|
||||||
|
}
|
||||||
284
src/parser.rs
Normal file
284
src/parser.rs
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use crate::lexer::{Lexer, Span, Spanned, Token};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Tuple<'a>(pub Vec<Spanned<Symbol<'a>>>);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Symbol<'a> {
|
||||||
|
Epsilon,
|
||||||
|
Ident(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Dest<'a> {
|
||||||
|
Ident(&'a str),
|
||||||
|
Function(Spanned<&'a str>, Spanned<Tuple<'a>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Item<'a> {
|
||||||
|
Symbol(Symbol<'a>),
|
||||||
|
Tuple(Tuple<'a>),
|
||||||
|
List(List<'a>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct List<'a>(pub Vec<Spanned<Item<'a>>>);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum TopLevel<'a> {
|
||||||
|
Assignment(Spanned<Dest<'a>>, Spanned<Item<'a>>),
|
||||||
|
Table(),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LogKind {
|
||||||
|
Lexer,
|
||||||
|
UnexpectedToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LogLevel {
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Log {
|
||||||
|
pub message: String,
|
||||||
|
pub range: Span,
|
||||||
|
pub level: LogLevel,
|
||||||
|
pub kind: LogKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
lexer: Peekable<Lexer<'a>>,
|
||||||
|
log: Vec<Log>,
|
||||||
|
eof: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
pub fn new(lexer: Lexer<'a>) -> Self{
|
||||||
|
Parser { eof: lexer.eof_span(), lexer: lexer.peekable(), log: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token(&mut self) -> Option<Spanned<Token<'a>>> {
|
||||||
|
loop {
|
||||||
|
match self.lexer.next()? {
|
||||||
|
Spanned(Ok(Token::Comment(_)), _) => {}
|
||||||
|
Spanned(Ok(ok), r) => return Some(Spanned(ok, r)),
|
||||||
|
Spanned(Err(err), r) => self.log.push(Log {
|
||||||
|
message: format!("{err:?}"),
|
||||||
|
range: r,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_token(&mut self) -> Option<Spanned<Token<'a>>> {
|
||||||
|
loop {
|
||||||
|
match *self.lexer.peek()? {
|
||||||
|
// not a heavy clone but because of range
|
||||||
|
Spanned(Ok(ok), r) => return Some(Spanned(ok, r)),
|
||||||
|
Spanned(Err(err), r) => self.log.push(Log {
|
||||||
|
message: format!("{err:?}"),
|
||||||
|
range: r,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_token(&mut self, expected: Token<'a>) -> (bool, Span) {
|
||||||
|
if let Some(Spanned(token, range)) = self.next_token() {
|
||||||
|
if token != expected {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!("unexpected token {:#}, expected {:}", token, expected),
|
||||||
|
range,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
(false, range)
|
||||||
|
}else{
|
||||||
|
(true, range)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!("unexpected eof expected {:#}", expected),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
(false, self.eof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_symbol(&mut self) -> Spanned<Symbol<'a>> {
|
||||||
|
match self.next_token() {
|
||||||
|
Some(Spanned(Token::Tilde, r)) => Spanned(Symbol::Epsilon, r),
|
||||||
|
Some(Spanned(Token::Ident("epsilon"), r)) => Spanned(Symbol::Epsilon, r),
|
||||||
|
Some(Spanned(Token::Ident("ε"), r)) => Spanned(Symbol::Epsilon, r),
|
||||||
|
Some(Spanned(Token::Ident(ident), r)) => Spanned(Symbol::Ident(ident), r),
|
||||||
|
Some(Spanned(got, r)) => {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected token {:#}, expected {:}|{:}",
|
||||||
|
got,
|
||||||
|
Token::Tilde,
|
||||||
|
Token::Ident("")
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
Spanned(Symbol::Ident("<INVALID>"), r)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected eof expected {:}|{:}",
|
||||||
|
Token::Tilde,
|
||||||
|
Token::Ident("")
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
Spanned(Symbol::Ident("<INVALID>"), self.eof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_tupple(&mut self) -> Spanned<Tuple<'a>> {
|
||||||
|
let mut items = Vec::new();
|
||||||
|
let (matched, start) = self.expect_token(Token::LPar);
|
||||||
|
if !matched{
|
||||||
|
return Spanned(Tuple(Vec::new()), start)
|
||||||
|
}
|
||||||
|
|
||||||
|
while !matches!(self.peek_token(), Some(Spanned(Token::RPar, _))) {
|
||||||
|
items.push(self.parse_symbol());
|
||||||
|
if matches!(self.peek_token(), Some(Spanned(Token::Comma, _))) {
|
||||||
|
self.next_token();
|
||||||
|
}
|
||||||
|
if self.peek_token().is_none(){
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected eof expected {:}",
|
||||||
|
Token::RPar
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, end) = self.expect_token(Token::RPar);
|
||||||
|
|
||||||
|
Spanned(Tuple(items), start.join(end))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_item(&mut self) -> Spanned<Item<'a>>{
|
||||||
|
match self.peek_token(){
|
||||||
|
Some(Spanned(Token::Ident(_)|Token::Tilde, _)) => self.parse_symbol().map(Item::Symbol),
|
||||||
|
Some(Spanned(Token::LPar, _)) => self.parse_tupple().map(Item::Tuple),
|
||||||
|
Some(Spanned(Token::LBrace, _)) => self.parse_list().map(Item::List),
|
||||||
|
Some(Spanned(got, r)) => {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected token {:#}, expected {:}|{:}|{:}|{:}",
|
||||||
|
got,
|
||||||
|
Token::Tilde,
|
||||||
|
Token::Ident(""),
|
||||||
|
Token::LPar,
|
||||||
|
Token::LBrace
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
Spanned(Item::Symbol(Symbol::Ident("<INVALID>")), r)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected eof expected {:}|{:}|{:}|{:}",
|
||||||
|
Token::Tilde,
|
||||||
|
Token::Ident(""),
|
||||||
|
Token::LPar,
|
||||||
|
Token::LBrace
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
Spanned(Item::Symbol(Symbol::Ident("<INVALID>")), self.eof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_list(&mut self) -> Spanned<List<'a>>{
|
||||||
|
let mut list = Vec::new();
|
||||||
|
let (matched, start) = self.expect_token(Token::LBrace);
|
||||||
|
if !matched{
|
||||||
|
return Spanned(List(Vec::new()), start)
|
||||||
|
}
|
||||||
|
|
||||||
|
while !matches!(self.peek_token(), Some(Spanned(Token::RBrace, _))) {
|
||||||
|
list.push(self.parse_item());
|
||||||
|
if matches!(self.peek_token(), Some(Spanned(Token::Comma, _))) {
|
||||||
|
self.next_token();
|
||||||
|
}
|
||||||
|
if self.peek_token().is_none(){
|
||||||
|
self.log.push(Log {
|
||||||
|
message: format!(
|
||||||
|
"unexpected eof expected {:}",
|
||||||
|
Token::RBrace
|
||||||
|
),
|
||||||
|
range: self.eof,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (_, end) = self.expect_token(Token::RBrace);
|
||||||
|
Spanned(List(list), start.join(end))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_elements(&mut self) -> Vec<Spanned<TopLevel<'a>>> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let Some(next) = self.next_token() else { break };
|
||||||
|
match next {
|
||||||
|
Spanned(Token::Ident(ident), ident_range) => {
|
||||||
|
let dest @ Spanned(_, start) = if matches!(self.peek_token(), Some(Spanned(Token::LPar, _))) {
|
||||||
|
let tuple = self.parse_tupple();
|
||||||
|
let span = ident_range.join(tuple.1);
|
||||||
|
Spanned(Dest::Function(Spanned(ident, ident_range), tuple), span)
|
||||||
|
} else {
|
||||||
|
Spanned(Dest::Ident(ident), ident_range)
|
||||||
|
};
|
||||||
|
self.expect_token(Token::Eq);
|
||||||
|
|
||||||
|
let item = self.parse_item();
|
||||||
|
let span = start.join(item.1);
|
||||||
|
result.push(Spanned(TopLevel::Assignment(dest, item), span));
|
||||||
|
}
|
||||||
|
_ => self.log.push(Log {
|
||||||
|
message: format!("unexpected token {:#}, expected {:}", next.0, Token::Ident("")),
|
||||||
|
range: next.1,
|
||||||
|
level: LogLevel::Error,
|
||||||
|
kind: LogKind::Lexer,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue