Browse Source

wip

master
Stephen 4 months ago
commit
c2ab8fead9
7 changed files with 582 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +185
    -0
      Cargo.lock
  3. +10
    -0
      Cargo.toml
  4. +2
    -0
      rustfmt.toml
  5. +64
    -0
      src/main.rs
  6. +255
    -0
      src/scanner.rs
  7. +64
    -0
      src/tokens.rs

+ 2
- 0
.gitignore View File

@ -0,0 +1,2 @@
/target
.idea

+ 185
- 0
Cargo.lock View File

@ -0,0 +1,185 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "getrandom"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "jlox"
version = "0.1.0"
dependencies = [
"phf",
]
[[package]]
name = "libc"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_shared",
"proc-macro-hack",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "ppv-lite86"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "proc-macro-hack"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
[[package]]
name = "proc-macro2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
]
[[package]]
name = "siphasher"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
[[package]]
name = "syn"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

+ 10
- 0
Cargo.toml View File

@ -0,0 +1,10 @@
[package]
name = "jlox"
version = "0.1.0"
authors = ["Stephen <webmaster@scd31.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
phf = { version = "0.8", features = ["macros"] }

+ 2
- 0
rustfmt.toml View File

@ -0,0 +1,2 @@
tab_spaces = 4
hard_tabs = true

+ 64
- 0
src/main.rs View File

@ -0,0 +1,64 @@
mod scanner;
mod tokens;
use std::io::Write;
use std::{env, fs, io};
fn main() {
let args: Vec<String> = env::args().collect();
match args.len() {
d if d > 2 => {
println!("Usage: {} [script]", args[0]);
}
2 => {
run_file(&args[1]);
}
_ => {
run_prompt();
}
}
}
fn run_file(path: &str) {
Interpreter::new().run(&fs::read_to_string(path).unwrap());
}
fn run_prompt() {
let mut interpreter = Interpreter::new();
loop {
let mut input = String::new();
print!("> ");
io::stdout().flush().unwrap();
match io::stdin().read_line(&mut input) {
Ok(_) => {
interpreter.run(&input);
interpreter.had_error = false;
}
Err(_) => return,
}
}
}
struct Interpreter {
had_error: bool,
}
impl Interpreter {
fn new() -> Self {
Self { had_error: false }
}
fn run(&mut self, source: &str) {
let tokens = source.split(' ');
for token in tokens {
println!("{}", token)
}
}
fn error(&mut self, line: usize, msg: &str) {
self.report(line, "", msg);
}
fn report(&mut self, line: usize, location: &str, msg: &str) {
println!("[line {}] Error {}: {}", line, location, msg);
}
}

+ 255
- 0
src/scanner.rs View File

@ -0,0 +1,255 @@
use crate::tokens::TokenType::{
And, Bang, BangEqual, Comma, Dot, Equal, EqualEqual, Greater, GreaterEqual, Identifier,
LeftBrace, LeftParen, Less, LessEqual, Minus, NoToken, Number, Plus, RightBrace, RightParen,
Semicolon, Slash, Star,
};
use crate::tokens::{Token, TokenType};
use phf::phf_map;
use std::str::FromStr;
static KEYWORDS: phf::Map<&'static str, TokenType> = phf_map! {
"and" => TokenType::And,
"class" => TokenType::Class,
"else" => TokenType::Else,
"false" => TokenType::False,
"for" => TokenType::For,
"fun" => TokenType::Fun,
"if" => TokenType::If,
"nil" => TokenType::Nil,
"or" => TokenType::Or,
"print" => TokenType::Print,
"return" => TokenType::Return,
"super" => TokenType::Super,
"this" => TokenType::This,
"true" => TokenType::True,
"var" => TokenType::Var,
"while" => TokenType::While
};
struct Scanner {
source: String,
tokens: Vec<Token>,
start: usize,
current: usize,
line: usize,
}
impl Scanner {
pub fn new(source: String) -> Self {
Self {
source,
tokens: Vec::new(),
start: 0,
current: 0,
line: 1,
}
}
pub fn scan_tokens(&mut self) -> Vec<Token> {
while !self.is_at_end() {
self.start = self.current;
self.scan_token();
}
self.tokens
.push(Token::new(TokenType::Eof, "".to_string(), self.line));
self.tokens.clone()
}
fn scan_token(&mut self) -> Result<(), (usize, String)> {
let c = self.advance();
let token = match c {
'(' => LeftParen,
')' => RightParen,
'{' => LeftBrace,
'}' => RightBrace,
',' => Comma,
'.' => Dot,
'-' => Minus,
'+' => Plus,
';' => Semicolon,
'*' => Star,
'!' => {
if self.match_next('=') {
BangEqual
} else {
Bang
}
}
'=' => {
if self.match_next('=') {
EqualEqual
} else {
Equal
}
}
'<' => {
if self.match_next('=') {
LessEqual
} else {
Less
}
}
'>' => {
if self.match_next('=') {
GreaterEqual
} else {
Greater
}
}
'/' => {
if self.match_next('/') {
// Handle comments
while self.peek() != '\n' && !self.is_at_end() {
self.advance();
}
NoToken
} else {
Slash
}
}
' ' | '\r' | '\t' => NoToken, // Whitespace
'\n' => {
self.line += 1;
NoToken
}
'"' => {
self.string();
NoToken
}
'0'..='9' => {
self.number();
NoToken
}
_ => {
if Scanner::is_alpha(c) {
self.identifier();
NoToken
} else {
return Err((self.line, "Unexpected character.".to_string()));
}
}
};
self.add_token(token);
Ok(())
}
fn is_at_end(&self) -> bool {
self.current >= self.source.len()
}
fn advance(&mut self) -> char {
self.current += 1;
self.source.chars().collect::<Vec<char>>()[self.current - 1]
}
fn add_token(&mut self, token_type: TokenType) {
if token_type == NoToken {
return;
}
let text: String = self.source.chars().collect::<Vec<char>>()[self.start..self.current]
.iter()
.collect();
self.tokens.push(Token::new(token_type, text, self.line));
}
fn match_next(&mut self, expected: char) -> bool {
if self.is_at_end() {
return false;
}
if self.source.chars().collect::<Vec<char>>()[self.current] != expected {
return false;
}
self.current += 1;
true
}
fn peek(&self) -> char {
if self.is_at_end() {
return '\0';
}
self.chars()[self.current]
}
fn peek_next(&self) -> char {
if self.current + 1 >= self.source.chars().count() {
return '\0';
}
self.chars()[self.current + 1]
}
fn string(&mut self) -> Result<(), (usize, String)> {
while self.peek() != '"' && !self.is_at_end() {
if self.peek() == '\n' {
self.line += 1;
}
self.advance();
}
if self.is_at_end() {
// Unterminated string
return Err((self.line, "Unterminated string.".to_string()));
}
self.advance(); // Closing "
let value: String = self.chars()[(self.start + 1)..(self.current - 1)]
.iter()
.collect();
self.add_token(TokenType::String(value));
Ok(())
}
fn chars(&self) -> Vec<char> {
self.source.chars().collect()
}
fn number(&mut self) {
while Scanner::is_digit(self.peek()) {}
if self.peek() == '.' && Scanner::is_digit(self.peek_next()) {
self.advance(); // Consume "."
while Scanner::is_digit(self.peek()) {} // Keep consuming digits
}
let s: String = self.chars()[(self.start + 1)..(self.current - 1)]
.iter()
.collect();
self.add_token(Number(f64::from_str(&s).unwrap()))
}
fn identifier(&mut self) {
while Scanner::is_alphanumeric(self.peek()) {
self.advance();
}
let text: String = self.chars()[self.start..self.current].iter().collect();
let token: TokenType = match KEYWORDS.get(text.as_ref()) {
Some(x) => x.clone(),
None => Identifier,
};
self.add_token(token);
}
// Struct methods
fn is_digit(c: char) -> bool {
('0'..='9').contains(&c)
}
fn is_alpha(c: char) -> bool {
('a'..='z').contains(&c) || ('A'..'Z').contains(&c) || c == '_'
}
fn is_alphanumeric(c: char) -> bool {
Scanner::is_digit(c) || Scanner::is_alpha(c)
}
}

+ 64
- 0
src/tokens.rs View File

@ -0,0 +1,64 @@
#[derive(Clone, PartialEq)]
pub enum TokenType {
LeftParen,
RightParen,
LeftBrace,
RightBrace,
Comma,
Dot,
Minus,
Plus,
Semicolon,
Slash,
Star,
Bang,
BangEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
Identifier,
String(String),
Number(f64),
And,
Class,
Else,
False,
Fun,
For,
If,
Nil,
Or,
Print,
Return,
Super,
This,
True,
Var,
While,
Eof,
NoToken,
}
#[derive(Clone)]
pub struct Token {
token_type: TokenType,
lexeme: String,
line: usize,
}
impl Token {
pub fn new(token_type: TokenType, lexeme: String, line: usize) -> Self {
Self {
token_type,
lexeme,
line,
}
}
}

Loading…
Cancel
Save