Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
target
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "tea"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "tea"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
|
||||
pub mod literal;
|
||||
use self::literal::parse_literal;
|
||||
pub mod identifier;
|
||||
use self::identifier::parse_identifier;
|
||||
pub mod operator;
|
||||
use self::operator::{ parse_operator, Operator };
|
||||
pub mod symbol;
|
||||
use self::symbol::{ parse_symbol, Symbol };
|
||||
pub mod keyword;
|
||||
use self::keyword::{ parse_keyword, Keyword };
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TeaValue {
|
||||
Int(i32),
|
||||
Uint(u32),
|
||||
Float(f32),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Token {
|
||||
Lit(TeaValue),
|
||||
Id(String),
|
||||
Kw(Keyword),
|
||||
Sym(Symbol),
|
||||
Op(Operator),
|
||||
}
|
||||
|
||||
/*#[derive(Debug)]
|
||||
pub struct LexError (String);*/
|
||||
|
||||
macro_rules! whitespace_chars {
|
||||
() => {
|
||||
' ' | '\t' | '\r' | '\n'
|
||||
}
|
||||
}
|
||||
pub(crate) use whitespace_chars;
|
||||
|
||||
fn skip_whitespace(input: &str) -> &str {
|
||||
let whitespace: String = input.chars().take_while(|c| {
|
||||
matches!(c, whitespace_chars!())
|
||||
}).collect();
|
||||
&input[whitespace.len()..]
|
||||
}
|
||||
|
||||
macro_rules! try_parse {
|
||||
($e:expr, $arr:ident, $str:ident) => {
|
||||
if let Some((v, s)) = $e {
|
||||
$str = s;
|
||||
$arr.push(v);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_tokens(s: &str) -> Result<Vec<Token>, (Vec<Token>, &str)> {
|
||||
let mut input = s;
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
loop {
|
||||
if input.is_empty() {
|
||||
return Ok(tokens);
|
||||
}
|
||||
try_parse!(parse_literal(input), tokens, input);
|
||||
try_parse!(parse_operator(input), tokens, input);
|
||||
try_parse!(parse_symbol(input), tokens, input);
|
||||
try_parse!(parse_keyword(input), tokens, input);
|
||||
try_parse!(parse_identifier(input), tokens, input);
|
||||
let post_whitespace = skip_whitespace(input);
|
||||
if post_whitespace != input {
|
||||
input = post_whitespace;
|
||||
continue;
|
||||
}
|
||||
return Err((tokens, "invalid input"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use super::{ Token };
|
||||
|
||||
macro_rules! ident_initial_chars {
|
||||
() => {
|
||||
'a' ..= 'z' | 'A' ..= 'Z'
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ident_chars {
|
||||
() => {
|
||||
ident_initial_chars!() | '0' ..= '9' | '$' | '_'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_identifier(input: &str) -> Option<(Token, &str)> {
|
||||
let mut chars = input.chars();
|
||||
let first_char: char = match chars.next() {
|
||||
a @ Some(ident_initial_chars!()) => a.unwrap(),
|
||||
_ => return None,
|
||||
};
|
||||
let rest: String = chars.take_while(|&c| matches!(c, ident_chars!())).collect();
|
||||
let full = format!("{}{}", first_char, rest);
|
||||
let len = full.len();
|
||||
Some((Token::Id(full), &input[len..]))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use super::{ whitespace_chars, Token };
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Keyword {
|
||||
Let,
|
||||
}
|
||||
|
||||
macro_rules! match_keyword {
|
||||
($str:literal, $kw:expr, $val:ident) => {
|
||||
if $val.starts_with($str) && matches!($val.chars().nth($str.len()), None | Some(whitespace_chars!())) {
|
||||
return Some(($kw, &$val[$str.len()..]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_keyword(input: &str) -> Option<(Token, &str)> {
|
||||
match_keyword!("let", Token::Kw(Keyword::Let), input);
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
use super::{ Token, TeaValue };
|
||||
|
||||
macro_rules! dec_chars {
|
||||
() => {
|
||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! hex_chars {
|
||||
() => {
|
||||
dec_chars!() | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
|
||||
}
|
||||
}
|
||||
fn parse_float(input: &str) -> Option<(f32, &str)> {
|
||||
let (input, sign) = if input.starts_with("-") {
|
||||
(input.strip_prefix("-").unwrap(), -1.0)
|
||||
} else {
|
||||
(input, 1.0)
|
||||
};
|
||||
let mut dec = false;
|
||||
let mut e = false;
|
||||
let s: String = input.chars().take_while(|&c| {
|
||||
if matches!(c, dec_chars!()) {
|
||||
return true;
|
||||
}
|
||||
match (dec, e) {
|
||||
(true, true) => false,
|
||||
(false, true) => {
|
||||
if c == '.' {
|
||||
dec = true;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
(true, false) => {
|
||||
if c == 'e' {
|
||||
e = true;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
(false, false) => {
|
||||
if c == '.' {
|
||||
dec = true;
|
||||
return true;
|
||||
}
|
||||
if c == 'e' {
|
||||
e = true;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
}
|
||||
}).collect();
|
||||
if s.is_empty() || !(dec || e) {
|
||||
return None;
|
||||
}
|
||||
Some((sign * s.parse::<f32>().unwrap(), &input[s.len()..]))
|
||||
}
|
||||
|
||||
fn parse_hex_int(input: &str) -> Option<(i32, &str)> {
|
||||
let (input, sign) = if input.starts_with("-") {
|
||||
(input.strip_prefix("-").unwrap(), -1)
|
||||
} else {
|
||||
(input, 1)
|
||||
};
|
||||
let input = input.strip_prefix("0x")?;
|
||||
let s: String = input.chars().take_while(|c| matches!(c, hex_chars!())).collect();
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some((sign * i32::from_str_radix(&s, 16).unwrap(), &input[s.len()..]))
|
||||
}
|
||||
|
||||
fn parse_dec_int(input: &str) -> Option<(i32, &str)> {
|
||||
let (input, sign) = if input.starts_with("-") {
|
||||
(input.strip_prefix("-").unwrap(), -1)
|
||||
} else {
|
||||
(input, 1)
|
||||
};
|
||||
let s: String = input.chars().take_while(|c| matches!(c, dec_chars!())).collect();
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some((sign * s.parse::<i32>().unwrap(), &input[s.len()..]))
|
||||
}
|
||||
|
||||
pub fn parse_literal(input: &str) -> Option<(Token, &str)> {
|
||||
if let Some((val, s)) = parse_float(input) {
|
||||
return Some((Token::Lit(TeaValue::Float(val)), s))
|
||||
}
|
||||
if let Some((val, s)) = parse_hex_int(input) {
|
||||
return Some((Token::Lit(TeaValue::Int(val)), s))
|
||||
}
|
||||
if let Some((val, s)) = parse_dec_int(input) {
|
||||
return Some((Token::Lit(TeaValue::Int(val)), s))
|
||||
}
|
||||
// TODO: other values
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::Token;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Operator {
|
||||
Plus,
|
||||
Minus,
|
||||
Star, // multiplication
|
||||
Slash, // division
|
||||
}
|
||||
|
||||
pub fn parse_operator(input: &str) -> Option<(Token, &str)> {
|
||||
let c: char = input.chars().next()?;
|
||||
let input = &input[1..];
|
||||
match c {
|
||||
'+' => Some((Token::Op(Operator::Plus), input)),
|
||||
'-' => Some((Token::Op(Operator::Minus), input)),
|
||||
'*' => Some((Token::Op(Operator::Star), input)),
|
||||
'/' => Some((Token::Op(Operator::Slash), input)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use super::Token;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Symbol {
|
||||
EqualSign,
|
||||
Semicolon,
|
||||
}
|
||||
|
||||
pub fn parse_symbol(input: &str) -> Option<(Token, &str)> {
|
||||
let c: char = input.chars().next()?;
|
||||
let input = &input[1..];
|
||||
match c {
|
||||
'=' => Some((Token::Sym(Symbol::EqualSign), input)),
|
||||
';' => Some((Token::Sym(Symbol::Semicolon), input)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
|
||||
use std::env::args;
|
||||
|
||||
#[macro_use]
|
||||
mod lex;
|
||||
use lex::parse_tokens;
|
||||
|
||||
fn main() {
|
||||
let args: String = args().skip(1).collect::<Vec<_>>().join(" ");
|
||||
let k = parse_tokens(&args);
|
||||
println!("{k:?}");
|
||||
}
|
||||
Reference in New Issue
Block a user