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