commit
10eefc0bab
12 changed files with 1755 additions and 0 deletions
24
.forgejo/workflows/rust.yaml
Normal file
24
.forgejo/workflows/rust.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup
|
||||||
|
run: DEBIAN_FRONTEND=noninteractive apt-get -yq install cargo
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
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 = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "holy_lisp"
|
||||||
|
version = "0.1.0"
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "holy_lisp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Apache <apache.software.foundation@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
57
README.md
Normal file
57
README.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Holy Lisp
|
||||||
|
|
||||||
|
<img src="./assets/holy_lisp.svg" alt="Holy Lisp Logo" width="100">
|
||||||
|
|
||||||
|
An opinionated lisp implementation developed in the most intuitive way I could come up with.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Lists with a function as the **first element** are evaluated when needed
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(print (add 1 2)) ; => 3
|
||||||
|
(print (1 2 3)) ; => (1 2 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quoted lists are **not evaluated**
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(print '(add 1 2)) ; => '(<function add> 1 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Elements within quoted lists can be unquoted
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(print
|
||||||
|
'(add
|
||||||
|
1 ,(sub 3 1)
|
||||||
|
)
|
||||||
|
) ; => '(<function add> 1 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Maps can be indexed with .
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
; In this example, ball_pos is a map
|
||||||
|
(print ball_pos.x ball_pos.y)
|
||||||
|
|
||||||
|
; This is just syntax sugar for
|
||||||
|
(get ball_pos "x")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions can be defined and called like so
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(fn hello(name) (
|
||||||
|
(print (concat "Hello, " name "!"))
|
||||||
|
))
|
||||||
|
|
||||||
|
(hello "Joe") ; => "Hello, Joe!"
|
||||||
|
|
||||||
|
|
||||||
|
; Anonymous function creation
|
||||||
|
(
|
||||||
|
(fn(x) (print x))
|
||||||
|
15
|
||||||
|
) ; => 15
|
||||||
|
```
|
65
assets/holy_lisp.svg
Normal file
65
assets/holy_lisp.svg
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
viewBox="0 0 52.916667 52.916666"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||||
|
sodipodi:docname="holy_lisp.svg"
|
||||||
|
inkscape:export-filename="holy_lisp.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#ffffff"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showborder="true"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="true"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="76.367532"
|
||||||
|
inkscape:cy="82.90827"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="path8"
|
||||||
|
style="fill:#d6af46;fill-opacity:1;stroke:none;stroke-width:0.358584"
|
||||||
|
d="m 12.05775,8.6112417 c 9.124696,-7.366724 22.333413,-6.6775948 30.641981,1.5986563 8.970994,8.971014 8.970995,23.515912 10e-7,32.486924 C 42.3473,43.022229 41.984755,43.336506 41.612644,43.63921 37.100709,47.109584 30.580116,46.817638 26.45627,42.697189 22.021071,38.261127 21.954802,30.987154 26.302434,26.486613 l -0.0095,-0.0095 c 0.03357,-0.02876 0.06694,-0.05775 0.100122,-0.08697 4.485724,-4.485533 4.485726,-11.758292 -1e-6,-16.243827 C 22.498755,6.2518505 16.505866,5.743428 12.057747,8.611236 Z" />
|
||||||
|
<path
|
||||||
|
id="circle8"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.179292"
|
||||||
|
d="m 10.212808,10.209898 c 0.58621,-0.5652085 1.202059,-1.0988492 1.844942,-1.5986563 4.448126,-2.867813 10.440999,-2.3593947 14.335303,1.5350763 4.485737,4.485544 4.485735,11.758283 1e-6,16.243826 -0.02517,0.0297 -0.08263,0.08801 -0.09064,0.09644 -4.347641,4.500551 -4.281371,11.774506 0.153838,16.210577 4.134425,4.131031 10.678664,4.415448 15.191818,0.916809 -9.078149,8.038955 -22.841226,7.637399 -31.435258,-0.91715 -8.9710021,-8.971022 -8.9710017,-23.515903 2e-6,-32.486925 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
<path
|
||||||
|
id="rect1"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke-width:0.293789"
|
||||||
|
d="m 37.722987,13.487549 v 5.191414 h -5.262211 v 1.695504 h 5.262211 v 11.861291 h 1.669665 V 20.374467 h 5.29115 v -1.695504 h -5.29115 v -5.191414 z" />
|
||||||
|
<path
|
||||||
|
id="path2"
|
||||||
|
style="fill:#d6af46;fill-opacity:1;stroke-width:0.293789"
|
||||||
|
d="m 13.525807,20.485768 v 5.191414 H 8.2635962 v 1.695504 h 5.2622108 v 11.86129 h 1.669665 v -11.86129 h 5.291149 v -1.695504 h -5.29115 v -5.191414 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
862
src/executor.rs
Normal file
862
src/executor.rs
Normal file
|
@ -0,0 +1,862 @@
|
||||||
|
use std::{collections::HashMap, fmt, time::Instant};
|
||||||
|
|
||||||
|
use crate::PreLispValue;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{LispState, LispValue, Parser, Tokenizer};
|
||||||
|
|
||||||
|
use super::add_function;
|
||||||
|
|
||||||
|
fn get_exec_result(source: &str) -> Result<Vec<LispValue>, Box<dyn std::error::Error>> {
|
||||||
|
let mut state = LispState::new();
|
||||||
|
|
||||||
|
state
|
||||||
|
.table
|
||||||
|
.insert(String::from("output"), LispValue::List(vec![], false));
|
||||||
|
add_function(&mut state.table, "print", |st, args| {
|
||||||
|
if let LispValue::List(v, _) = st.table.get_mut("output").unwrap() {
|
||||||
|
v.push(LispValue::List(args, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec![LispValue::Nil])
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut tokenizer = Tokenizer::new(String::from(source));
|
||||||
|
let mut parser = Parser::new(tokenizer.tokenize()?);
|
||||||
|
let instructions = parser.parse();
|
||||||
|
|
||||||
|
state.execute(instructions)?;
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
if let LispValue::List(outerlist, _) = &state.table["output"] {
|
||||||
|
for list in outerlist {
|
||||||
|
if let LispValue::List(innerlist, _) = list {
|
||||||
|
for innervalue in innerlist {
|
||||||
|
out.push(innervalue.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello_world() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![LispValue::String(String::from("Hello, World!"))],
|
||||||
|
get_exec_result(
|
||||||
|
"
|
||||||
|
(print \"Hello, World!\")
|
||||||
|
"
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![LispValue::Float(16.05)],
|
||||||
|
get_exec_result(
|
||||||
|
"
|
||||||
|
(print
|
||||||
|
(add
|
||||||
|
(div
|
||||||
|
4.5
|
||||||
|
(sub 0.5 0.2)
|
||||||
|
)
|
||||||
|
(mul 21 0.05)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LispState {
|
||||||
|
table: HashMap<String, LispValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RuntimeError {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Runtime Error: {}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RuntimeError {}
|
||||||
|
|
||||||
|
type RustFunctionType = fn(&mut LispState, Vec<LispValue>) -> Result<Vec<LispValue>, RuntimeError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum LispValue {
|
||||||
|
Nil,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
String(String),
|
||||||
|
Integer(i32),
|
||||||
|
Float(f32),
|
||||||
|
List(Vec<LispValue>, bool),
|
||||||
|
Instant(Instant),
|
||||||
|
LispFunction(String, Vec<String>, Vec<LispValue>),
|
||||||
|
RustFunction(String, RustFunctionType),
|
||||||
|
Map(HashMap<String, LispValue>),
|
||||||
|
Var(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LispValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
match self {
|
||||||
|
LispValue::Nil => write!(f, "nil"),
|
||||||
|
LispValue::True => write!(f, "true"),
|
||||||
|
LispValue::False => write!(f, "false"),
|
||||||
|
LispValue::String(str) => write!(f, "\"{}\"", str),
|
||||||
|
LispValue::LispFunction(name, _, _) => write!(f, "<function {}>", name),
|
||||||
|
LispValue::RustFunction(name, _) => write!(f, "<function {}>", name),
|
||||||
|
LispValue::Integer(num) => write!(f, "{}", num),
|
||||||
|
LispValue::Float(num) => write!(f, "{}", num),
|
||||||
|
LispValue::List(elems, quoted) => {
|
||||||
|
let mut strs = Vec::new();
|
||||||
|
|
||||||
|
for e in elems {
|
||||||
|
strs.push(e.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if *quoted {
|
||||||
|
write!(f, "'({})", strs.join(" "))
|
||||||
|
} else {
|
||||||
|
write!(f, "({})", strs.join(" "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val => {
|
||||||
|
write!(f, "{}", format!("{:?}", val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lisp_display(v: LispValue) -> String {
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
match v {
|
||||||
|
LispValue::Nil => String::from("nil"),
|
||||||
|
LispValue::True => String::from("true"),
|
||||||
|
LispValue::False => String::from("false"),
|
||||||
|
LispValue::String(str) => str,
|
||||||
|
LispValue::LispFunction(name, _, _) => format!("<function {}>", name),
|
||||||
|
LispValue::RustFunction(name, _) => format!("<function {}>", name),
|
||||||
|
LispValue::Integer(num) => num.to_string(),
|
||||||
|
LispValue::Float(num) => num.to_string(),
|
||||||
|
LispValue::List(elems, quoted) => {
|
||||||
|
let mut strs = Vec::new();
|
||||||
|
|
||||||
|
for e in elems {
|
||||||
|
strs.push(e.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
format!("'({})", strs.join(" "))
|
||||||
|
} else {
|
||||||
|
format!("({})", strs.join(" "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val => {
|
||||||
|
format!("{}", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(v: &LispValue) -> String {
|
||||||
|
match v {
|
||||||
|
LispValue::Nil => String::from("nil"),
|
||||||
|
LispValue::True => String::from("bool"),
|
||||||
|
LispValue::False => String::from("bool"),
|
||||||
|
LispValue::Integer(_) => String::from("int"),
|
||||||
|
LispValue::Float(_) => String::from("float"),
|
||||||
|
LispValue::String(_) => String::from("string"),
|
||||||
|
LispValue::List(_, _) => String::from("list"),
|
||||||
|
LispValue::Instant(_) => String::from("instant"),
|
||||||
|
LispValue::LispFunction(_, _, _) => String::from("function"),
|
||||||
|
LispValue::RustFunction(_, _) => String::from("function"),
|
||||||
|
LispValue::Map(_) => String::from("map"),
|
||||||
|
LispValue::Var(_) => String::from("symbol"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_falsy(v: &LispValue) -> bool {
|
||||||
|
match v {
|
||||||
|
LispValue::Nil => true,
|
||||||
|
LispValue::False => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_truthy(v: &LispValue) -> bool {
|
||||||
|
!is_falsy(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for LispValue {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
LispValue::String(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<String> for LispValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
LispValue::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<i32> for LispValue {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
LispValue::Integer(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<f32> for LispValue {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
LispValue::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<bool> for LispValue {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
match value {
|
||||||
|
true => LispValue::True,
|
||||||
|
false => LispValue::False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Vec<LispValue>> for LispValue {
|
||||||
|
fn from(value: Vec<LispValue>) -> Self {
|
||||||
|
LispValue::List(value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_function<T: Into<String> + Clone>(
|
||||||
|
table: &mut HashMap<String, LispValue>,
|
||||||
|
name: T,
|
||||||
|
func: RustFunctionType,
|
||||||
|
) {
|
||||||
|
table.insert(
|
||||||
|
name.clone().into(),
|
||||||
|
LispValue::RustFunction(name.into(), func),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LispState {
|
||||||
|
pub fn new() -> LispState {
|
||||||
|
let mut table = HashMap::new();
|
||||||
|
|
||||||
|
add_function(&mut table, "now", |_, _| {
|
||||||
|
Ok(vec![LispValue::Instant(Instant::now())])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "since", |_, args| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to since (expected 1, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let LispValue::Instant(i) = args[0] {
|
||||||
|
Ok(vec![LispValue::Float(
|
||||||
|
Instant::now().duration_since(i).as_secs_f32(),
|
||||||
|
)])
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Bad arguments to since (expected instant, got {})",
|
||||||
|
get_type(&args[0])
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "type", |_, args| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to type (expected 1, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(vec![LispValue::String(get_type(&args[0]))])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "sleep", |_, args| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to sleep (expected 1, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let LispValue::Float(n) = args[0] {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs_f32(n));
|
||||||
|
} else if let LispValue::Integer(n) = args[0] {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(n as u64));
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Bad arguments to sleep (expected float or int, got {})",
|
||||||
|
get_type(&args[0])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec![LispValue::Nil])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "or", |_, args| {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to or (expected 2, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if is_truthy(&args[0]) {
|
||||||
|
Ok(vec![args[0].clone()])
|
||||||
|
} else {
|
||||||
|
Ok(vec![args[1].clone()])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "and", |_, args| {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to and (expected 2, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if is_truthy(&args[0]) {
|
||||||
|
Ok(vec![args[1].clone()])
|
||||||
|
} else {
|
||||||
|
Ok(vec![args[0].clone()])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "echo", |_, args| {
|
||||||
|
return Ok(args);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "set", |state, args| {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to set (expected 2, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let LispValue::String(name) = &args[0] {
|
||||||
|
state.table.insert(name.to_owned(), args[1].clone());
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Bad argument 1 to set (expected string, got {})",
|
||||||
|
get_type(&args[0])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec![args[1].clone()])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "get", |_, args| {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!("Bad arguments to get (expected 2, got {})", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let LispValue::Map(map) = &args[0] {
|
||||||
|
if let LispValue::String(str) = &args[1] {
|
||||||
|
return Ok(vec![map.get(str).unwrap_or(&LispValue::Nil).to_owned()]);
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Bad argument 2 to get (expected string, got {})",
|
||||||
|
get_type(&args[0])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Bad argument 1 to get (expected map, got {})",
|
||||||
|
get_type(&args[0])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "print", |_, args| {
|
||||||
|
let mut strings = Vec::new();
|
||||||
|
for val in args {
|
||||||
|
strings.push(lisp_display(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = strings.join(" ");
|
||||||
|
println!("{}", str);
|
||||||
|
|
||||||
|
Ok(vec![LispValue::Nil])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "concat", |_, args| {
|
||||||
|
let mut strings = Vec::new();
|
||||||
|
for val in args {
|
||||||
|
strings.push(lisp_display(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = strings.concat();
|
||||||
|
Ok(vec![LispValue::String(str)])
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "add", |_, args| {
|
||||||
|
if args.len() < 1 {
|
||||||
|
return Ok(vec![LispValue::Nil]);
|
||||||
|
} else if args.len() == 1 {
|
||||||
|
return Ok(vec![args[0].clone()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t = "int";
|
||||||
|
for v in &args {
|
||||||
|
match v {
|
||||||
|
LispValue::Float(_) => t = "float",
|
||||||
|
LispValue::Integer(_) => (),
|
||||||
|
_ => {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to 'add' (expected float or int, got {}",
|
||||||
|
get_type(v)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match t {
|
||||||
|
"int" => {
|
||||||
|
let mut sum = 0;
|
||||||
|
for v in &args {
|
||||||
|
if let LispValue::Integer(n) = v {
|
||||||
|
sum += n;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Integer(sum)]);
|
||||||
|
}
|
||||||
|
"float" => {
|
||||||
|
let mut sum = 0.0 as f32;
|
||||||
|
for v in &args {
|
||||||
|
if let LispValue::Float(n) = v {
|
||||||
|
sum += n;
|
||||||
|
} else if let LispValue::Integer(n) = v {
|
||||||
|
sum += *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(sum)]);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "sub", |_, args| {
|
||||||
|
if args.len() < 1 {
|
||||||
|
return Ok(vec![LispValue::Nil]);
|
||||||
|
} else if args.len() == 1 {
|
||||||
|
return Ok(vec![args[0].clone()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t = "int";
|
||||||
|
for v in &args {
|
||||||
|
match v {
|
||||||
|
LispValue::Float(_) => t = "float",
|
||||||
|
LispValue::Integer(_) => (),
|
||||||
|
_ => {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to 'sub' (expected float or int, got {}",
|
||||||
|
get_type(v)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match t {
|
||||||
|
"int" => {
|
||||||
|
if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut sum = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
|
sum -= n;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Integer(sum)]);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"float" => {
|
||||||
|
if let LispValue::Float(n) = args[0] {
|
||||||
|
let mut sum = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
sum -= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
sum -= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(sum)]);
|
||||||
|
} else if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut sum = n as f32;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
sum -= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
sum -= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(sum)]);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "mul", |_, args| {
|
||||||
|
if args.len() < 1 {
|
||||||
|
return Ok(vec![LispValue::Nil]);
|
||||||
|
} else if args.len() == 1 {
|
||||||
|
return Ok(vec![args[0].clone()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t = "int";
|
||||||
|
for v in &args {
|
||||||
|
match v {
|
||||||
|
LispValue::Float(_) => t = "float",
|
||||||
|
LispValue::Integer(_) => (),
|
||||||
|
_ => {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to 'mul' (expected float or int, got {}",
|
||||||
|
get_type(v)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match t {
|
||||||
|
"int" => {
|
||||||
|
if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut sum = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
|
sum *= n;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Integer(sum)]);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"float" => {
|
||||||
|
if let LispValue::Float(n) = args[0] {
|
||||||
|
let mut product = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
product *= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
product *= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(product)]);
|
||||||
|
} else if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut product = n as f32;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
product *= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
product *= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(product)]);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_function(&mut table, "div", |_, args| {
|
||||||
|
if args.len() < 1 {
|
||||||
|
return Ok(vec![LispValue::Nil]);
|
||||||
|
} else if args.len() == 1 {
|
||||||
|
return Ok(vec![args[0].clone()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t = "int";
|
||||||
|
for v in &args {
|
||||||
|
match v {
|
||||||
|
LispValue::Float(_) => t = "float",
|
||||||
|
LispValue::Integer(_) => (),
|
||||||
|
_ => {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to 'div' (expected float or int, got {}",
|
||||||
|
get_type(v)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match t {
|
||||||
|
"int" => {
|
||||||
|
if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut sum = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
|
sum /= n;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Integer(sum)]);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"float" => {
|
||||||
|
if let LispValue::Float(n) = args[0] {
|
||||||
|
let mut product = n;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
product /= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
product /= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(product)]);
|
||||||
|
} else if let LispValue::Integer(n) = args[0] {
|
||||||
|
let mut product = n as f32;
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if let LispValue::Float(n) = &args[i] {
|
||||||
|
product /= *n;
|
||||||
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
|
product /= *n as f32;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(vec![LispValue::Float(product)]);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LispState { table }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_prevalues(&self, input: Vec<PreLispValue>) -> Vec<LispValue> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
for v in input {
|
||||||
|
out.push(match v {
|
||||||
|
PreLispValue::Nil => LispValue::Nil,
|
||||||
|
PreLispValue::True => LispValue::True,
|
||||||
|
PreLispValue::False => LispValue::False,
|
||||||
|
PreLispValue::String(str) => LispValue::String(str),
|
||||||
|
PreLispValue::Integer(num) => LispValue::Integer(num),
|
||||||
|
PreLispValue::Float(num) => LispValue::Float(num),
|
||||||
|
PreLispValue::List(list, quoted) => {
|
||||||
|
LispValue::List(Self::handle_prevalues(self, list), quoted)
|
||||||
|
}
|
||||||
|
PreLispValue::LispFunction(name, arg_names, instructions) => {
|
||||||
|
LispValue::LispFunction(
|
||||||
|
name,
|
||||||
|
arg_names,
|
||||||
|
Self::handle_prevalues(self, instructions),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PreLispValue::Var(name) => LispValue::Var(name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_list(
|
||||||
|
&mut self,
|
||||||
|
env: &mut HashMap<String, LispValue>,
|
||||||
|
list: &Vec<LispValue>,
|
||||||
|
quoted: bool,
|
||||||
|
) -> Result<(Vec<LispValue>, bool), RuntimeError> {
|
||||||
|
if list.len() < 1 {
|
||||||
|
return Ok((vec![LispValue::List(Vec::new(), quoted)], false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = &mut list.clone();
|
||||||
|
|
||||||
|
for i in 0..list.len() {
|
||||||
|
if let LispValue::Var(name) = &list[i] {
|
||||||
|
list[i] = env
|
||||||
|
.get(name)
|
||||||
|
.unwrap_or_else(|| self.table.get(name).unwrap_or(&LispValue::Nil))
|
||||||
|
.to_owned();
|
||||||
|
}
|
||||||
|
if let LispValue::List(elements, quoted) = &list[i] {
|
||||||
|
let (elems, astuple) = self.eval_list(env, elements, *quoted)?;
|
||||||
|
if astuple {
|
||||||
|
list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned();
|
||||||
|
for j in 1..elems.len() {
|
||||||
|
list.insert(i, elems[j].to_owned());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list[i] = LispValue::List(elems, *quoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
return Ok((list.to_vec(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let LispValue::RustFunction(_name, f) = &list[0] {
|
||||||
|
Ok((f(self, list[1..].to_vec())?, true))
|
||||||
|
} else if let LispValue::LispFunction(_name, _, _) = &list[0] {
|
||||||
|
Ok((
|
||||||
|
self.call_lisp_func_internal(env, &list[0], list[1..].to_vec())?,
|
||||||
|
true,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok((list.to_vec(), false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_lisp_func_internal(
|
||||||
|
&mut self,
|
||||||
|
env: &mut HashMap<String, LispValue>,
|
||||||
|
f: &LispValue,
|
||||||
|
args: Vec<LispValue>,
|
||||||
|
) -> Result<Vec<LispValue>, RuntimeError> {
|
||||||
|
let (arg_names, instructions) = match f {
|
||||||
|
LispValue::LispFunction(_, arg_names, i) => (arg_names, i),
|
||||||
|
_ => {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to call_lisp_func_internal (expected function, got {})",
|
||||||
|
get_type(f)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..arg_names.len() {
|
||||||
|
env.insert(
|
||||||
|
arg_names[i].to_owned(),
|
||||||
|
args.get(i).unwrap_or(&LispValue::Nil).to_owned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for l in instructions {
|
||||||
|
if let LispValue::List(elements, quoted) = l {
|
||||||
|
let (values, astuple) = self.eval_list(env, &elements, *quoted)?;
|
||||||
|
if values.len() > 0 && values[0] != LispValue::Nil {
|
||||||
|
if astuple {
|
||||||
|
for v in values {
|
||||||
|
out.push(v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.push(values.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_lisp_func(
|
||||||
|
&mut self,
|
||||||
|
f: &LispValue,
|
||||||
|
args: Vec<impl Into<LispValue>>,
|
||||||
|
) -> Result<Vec<LispValue>, RuntimeError> {
|
||||||
|
if !matches!(f, LispValue::LispFunction(_, _, _)) {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
message: format!(
|
||||||
|
"Invalid argument to call_lisp_func_internal (expected function, got {})",
|
||||||
|
get_type(&f)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vargs = Vec::new();
|
||||||
|
for v in args {
|
||||||
|
vargs.push(v.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.call_lisp_func_internal(&mut HashMap::new(), &f, vargs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_func(&mut self, name: impl Into<String>) -> Option<LispValue> {
|
||||||
|
if let Some(v) = self.table.get(&name.into()) {
|
||||||
|
Some(v.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(
|
||||||
|
&mut self,
|
||||||
|
instructions: Vec<PreLispValue>,
|
||||||
|
) -> Result<Vec<LispValue>, RuntimeError> {
|
||||||
|
let instructions = Self::handle_prevalues(self, instructions);
|
||||||
|
|
||||||
|
let mut outs = Vec::new();
|
||||||
|
|
||||||
|
let mut env = HashMap::new();
|
||||||
|
|
||||||
|
for val in instructions {
|
||||||
|
if let LispValue::List(elements, quoted) = val {
|
||||||
|
let (vals, astuple) = self.eval_list(&mut env, &elements, quoted)?;
|
||||||
|
if astuple {
|
||||||
|
for val in vals {
|
||||||
|
outs.push(val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outs.push(vals.into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outs.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(outs)
|
||||||
|
}
|
||||||
|
}
|
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
mod tokenizer;
|
||||||
|
pub use tokenizer::*;
|
||||||
|
|
||||||
|
mod parser;
|
||||||
|
pub use parser::*;
|
||||||
|
|
||||||
|
mod executor;
|
||||||
|
pub use executor::*;
|
23
src/main.rs
Normal file
23
src/main.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
mod tokenizer;
|
||||||
|
use tokenizer::*;
|
||||||
|
|
||||||
|
mod parser;
|
||||||
|
use parser::*;
|
||||||
|
|
||||||
|
mod executor;
|
||||||
|
use executor::*;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let source = std::fs::read_to_string("src/test.lisp").unwrap();
|
||||||
|
|
||||||
|
let mut tokenizer = Tokenizer::new(source);
|
||||||
|
let tokens = tokenizer.tokenize()?;
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let instructions = parser.parse();
|
||||||
|
|
||||||
|
let mut state = LispState::new();
|
||||||
|
|
||||||
|
state.execute(instructions)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
379
src/parser.rs
Normal file
379
src/parser.rs
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{Parser, PreLispValue, Tokenizer, TokenizerError};
|
||||||
|
|
||||||
|
fn quick_parse(source: &str) -> Result<Vec<PreLispValue>, TokenizerError> {
|
||||||
|
let mut tokenizer = Tokenizer::new(String::from(source));
|
||||||
|
let tokens = tokenizer.tokenize()?;
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
Ok(parser.parse())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var(name: &str) -> PreLispValue {
|
||||||
|
PreLispValue::Var(String::from(name))
|
||||||
|
}
|
||||||
|
fn string(s: &str) -> PreLispValue {
|
||||||
|
PreLispValue::String(String::from(s))
|
||||||
|
}
|
||||||
|
fn list(v: Vec<PreLispValue>, quoted: bool) -> PreLispValue {
|
||||||
|
PreLispValue::List(v, quoted)
|
||||||
|
}
|
||||||
|
fn int(n: i32) -> PreLispValue {
|
||||||
|
PreLispValue::Integer(n)
|
||||||
|
}
|
||||||
|
fn float(n: f32) -> PreLispValue {
|
||||||
|
PreLispValue::Float(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello_world() -> Result<(), TokenizerError> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![list(vec![var("print"), string("Hello, World!")], false)],
|
||||||
|
quick_parse(
|
||||||
|
"
|
||||||
|
(print \"Hello, World!\")
|
||||||
|
"
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math() -> Result<(), TokenizerError> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![list(
|
||||||
|
vec![
|
||||||
|
var("print"),
|
||||||
|
list(
|
||||||
|
vec![
|
||||||
|
var("add"),
|
||||||
|
list(
|
||||||
|
vec![
|
||||||
|
var("div"),
|
||||||
|
float(4.5),
|
||||||
|
list(vec![var("sub"), float(0.5), float(0.2)], false)
|
||||||
|
],
|
||||||
|
false
|
||||||
|
),
|
||||||
|
list(vec![var("mul"), int(21), float(0.05)], false)
|
||||||
|
],
|
||||||
|
false
|
||||||
|
)
|
||||||
|
],
|
||||||
|
false
|
||||||
|
)],
|
||||||
|
quick_parse(
|
||||||
|
"
|
||||||
|
(print
|
||||||
|
(add
|
||||||
|
(div
|
||||||
|
4.5
|
||||||
|
(sub 0.5 0.2)
|
||||||
|
)
|
||||||
|
(mul 21 0.05)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::Token;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum PreLispValue {
|
||||||
|
Nil,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
String(String),
|
||||||
|
Var(String),
|
||||||
|
Integer(i32),
|
||||||
|
Float(f32),
|
||||||
|
List(Vec<PreLispValue>, bool), // Values, Quoted
|
||||||
|
LispFunction(String, Vec<String>, Vec<PreLispValue>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PreLispValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PreLispValue::Nil => write!(f, "nil"),
|
||||||
|
PreLispValue::True => write!(f, "true"),
|
||||||
|
PreLispValue::False => write!(f, "false"),
|
||||||
|
PreLispValue::String(str) => write!(f, "{}", str),
|
||||||
|
PreLispValue::LispFunction(name, _, _) => write!(f, "<function {}>", name),
|
||||||
|
PreLispValue::Integer(num) => write!(f, "{}", num),
|
||||||
|
PreLispValue::Float(num) => write!(f, "{}", num),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for PreLispValue {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
PreLispValue::String(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<String> for PreLispValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
PreLispValue::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<i32> for PreLispValue {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
PreLispValue::Integer(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<f32> for PreLispValue {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
PreLispValue::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Vec<PreLispValue>> for PreLispValue {
|
||||||
|
fn from(value: Vec<PreLispValue>) -> Self {
|
||||||
|
PreLispValue::List(value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle failure
|
||||||
|
fn read_exp(open_paren_idx: usize, tokens: &Vec<Token>) -> Option<(Vec<Token>, usize)> /* Option<tokens, close_paren_idx> */
|
||||||
|
{
|
||||||
|
let mut tkns = Vec::new();
|
||||||
|
let mut depth = 0;
|
||||||
|
|
||||||
|
for i in open_paren_idx..tokens.len() {
|
||||||
|
match &tokens[i] {
|
||||||
|
Token::OpenParen => {
|
||||||
|
if depth != 0 {
|
||||||
|
tkns.push(Token::OpenParen);
|
||||||
|
}
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
Token::CloseParen => {
|
||||||
|
depth -= 1;
|
||||||
|
if depth == 0 {
|
||||||
|
return Some((tkns, i));
|
||||||
|
} else {
|
||||||
|
tkns.push(Token::CloseParen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token => {
|
||||||
|
tkns.push(token.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
|
||||||
|
let mut values = Vec::new();
|
||||||
|
|
||||||
|
if tokens.len() < 1 {
|
||||||
|
return PreLispValue::List(values, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut quote_next_list = false;
|
||||||
|
let mut unquote_next_list = false;
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < tokens.len() {
|
||||||
|
let tkn = &tokens[i];
|
||||||
|
match tkn {
|
||||||
|
Token::Quote => {
|
||||||
|
quote_next_list = true;
|
||||||
|
}
|
||||||
|
Token::Unquote => {
|
||||||
|
unquote_next_list = true;
|
||||||
|
}
|
||||||
|
Token::OpenParen => {
|
||||||
|
let (tkns, close_paren_idx) = read_exp(i, &tokens).unwrap();
|
||||||
|
values.push(parse_exp(
|
||||||
|
tkns,
|
||||||
|
if unquote_next_list {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
quote_next_list || quoted
|
||||||
|
},
|
||||||
|
));
|
||||||
|
i = close_paren_idx;
|
||||||
|
quote_next_list = false;
|
||||||
|
unquote_next_list = false;
|
||||||
|
}
|
||||||
|
Token::Identifier(name) => match name.as_str() {
|
||||||
|
"nil" => values.push(PreLispValue::Nil),
|
||||||
|
"true" => values.push(PreLispValue::True),
|
||||||
|
"false" => values.push(PreLispValue::False),
|
||||||
|
_ => values.push(PreLispValue::Var(name.to_owned())),
|
||||||
|
},
|
||||||
|
Token::Index(map, idx) => {
|
||||||
|
values.push(PreLispValue::List(
|
||||||
|
vec![
|
||||||
|
PreLispValue::Var(String::from("get")),
|
||||||
|
PreLispValue::Var(map.to_owned()),
|
||||||
|
PreLispValue::String(idx.to_owned()),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Token::Integer(num) => {
|
||||||
|
values.push(PreLispValue::Integer(*num));
|
||||||
|
}
|
||||||
|
Token::Float(num) => {
|
||||||
|
values.push(PreLispValue::Float(*num));
|
||||||
|
}
|
||||||
|
Token::String(str) => {
|
||||||
|
values.push(PreLispValue::String(str.to_owned()));
|
||||||
|
}
|
||||||
|
Token::CloseParen => {
|
||||||
|
panic!("Unexpected closing parenthesis");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreLispValue::List(values, quoted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
macros: HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_macro(
|
||||||
|
macros: &mut HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||||
|
name: impl Into<String>,
|
||||||
|
mac: fn(Vec<PreLispValue>) -> Vec<PreLispValue>,
|
||||||
|
) {
|
||||||
|
macros.insert(name.into(), mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn new(tokens: Vec<Token>) -> Parser {
|
||||||
|
let mut macros = HashMap::new();
|
||||||
|
|
||||||
|
add_macro(&mut macros, "set", |x| {
|
||||||
|
if x.len() != 3
|
||||||
|
|| (!matches!(x[1], PreLispValue::Var(_))
|
||||||
|
&& !matches!(x[1], PreLispValue::String(_)))
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list = x.clone();
|
||||||
|
|
||||||
|
if let PreLispValue::Var(name) = &list[1] {
|
||||||
|
list[1] = PreLispValue::String(name.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
list
|
||||||
|
});
|
||||||
|
|
||||||
|
add_macro(&mut macros, "fn", |x| {
|
||||||
|
if x.len() < 3 || x.len() > 4 {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name: String = String::new();
|
||||||
|
let mut args: &Vec<PreLispValue> = &Vec::new();
|
||||||
|
let mut list: &Vec<PreLispValue> = &Vec::new();
|
||||||
|
|
||||||
|
if x.len() == 3 {
|
||||||
|
if !matches!(x[1], PreLispValue::List(_, _))
|
||||||
|
|| !matches!(x[2], PreLispValue::List(_, _))
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
name = String::from("Anonymous");
|
||||||
|
if let PreLispValue::List(l, _) = &x[1] {
|
||||||
|
args = l;
|
||||||
|
}
|
||||||
|
if let PreLispValue::List(l, _) = &x[2] {
|
||||||
|
list = l;
|
||||||
|
}
|
||||||
|
} else if x.len() == 4 {
|
||||||
|
if !matches!(x[1], PreLispValue::Var(_))
|
||||||
|
|| !matches!(x[2], PreLispValue::List(_, _))
|
||||||
|
|| !matches!(x[3], PreLispValue::List(_, _))
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
if let PreLispValue::Var(n) = &x[1] {
|
||||||
|
name = n.to_owned();
|
||||||
|
}
|
||||||
|
if let PreLispValue::List(l, _) = &x[2] {
|
||||||
|
args = l;
|
||||||
|
}
|
||||||
|
if let PreLispValue::List(l, _) = &x[3] {
|
||||||
|
list = l;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut arg_names = Vec::new();
|
||||||
|
for arg in args {
|
||||||
|
if let PreLispValue::Var(n) = arg {
|
||||||
|
arg_names.push(n.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = PreLispValue::LispFunction(name.clone(), arg_names, list.to_owned());
|
||||||
|
|
||||||
|
if x.len() == 4 {
|
||||||
|
return vec![PreLispValue::Var(String::from("set")), name.into(), func];
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec![PreLispValue::Var(String::from("echo")), func];
|
||||||
|
});
|
||||||
|
|
||||||
|
Parser { tokens, macros }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn macro_check(&mut self, input: Vec<PreLispValue>, quoted: bool) -> Vec<PreLispValue> {
|
||||||
|
if quoted || input.len() < 1 {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = input.clone();
|
||||||
|
|
||||||
|
for i in 0..output.len() {
|
||||||
|
if let PreLispValue::List(elems, quoted) = &output[i] {
|
||||||
|
output[i] = PreLispValue::List(self.macro_check(elems.to_vec(), *quoted), *quoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let PreLispValue::Var(name) = &output[0] {
|
||||||
|
if self.macros.contains_key(name) {
|
||||||
|
return self.macros.get(name).unwrap()(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Vec<PreLispValue> {
|
||||||
|
let mut values = Vec::new();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.tokens.len() {
|
||||||
|
match &self.tokens[i] {
|
||||||
|
Token::OpenParen => {
|
||||||
|
let (tkns, close_paren_idx) = read_exp(i, &self.tokens).unwrap();
|
||||||
|
values.push(parse_exp(tkns, false));
|
||||||
|
i = close_paren_idx;
|
||||||
|
}
|
||||||
|
tkn => {
|
||||||
|
// TODO: Include more error data
|
||||||
|
panic!("Unexpected token {:?}", tkn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.macro_check(values, false)
|
||||||
|
}
|
||||||
|
}
|
13
src/test.lisp
Normal file
13
src/test.lisp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(fn countuntil(until n) (
|
||||||
|
(set n (add (or n 0) 1))
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(set start (now))
|
||||||
|
|
||||||
|
(countuntil 100)
|
||||||
|
|
||||||
|
(print (since start))
|
||||||
|
|
||||||
|
|
309
src/tokenizer.rs
Normal file
309
src/tokenizer.rs
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{Token, Tokenizer, TokenizerError};
|
||||||
|
|
||||||
|
fn quick_tokenize(source: &str) -> Result<Vec<Token>, TokenizerError> {
|
||||||
|
let mut tokenizer = Tokenizer::new(String::from(source));
|
||||||
|
return tokenizer.tokenize();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open() -> Token {
|
||||||
|
Token::OpenParen
|
||||||
|
}
|
||||||
|
fn close() -> Token {
|
||||||
|
Token::CloseParen
|
||||||
|
}
|
||||||
|
fn iden(s: &str) -> Token {
|
||||||
|
Token::Identifier(String::from(s))
|
||||||
|
}
|
||||||
|
fn string(s: &str) -> Token {
|
||||||
|
Token::String(String::from(s))
|
||||||
|
}
|
||||||
|
fn int(n: i32) -> Token {
|
||||||
|
Token::Integer(n)
|
||||||
|
}
|
||||||
|
fn float(n: f32) -> Token {
|
||||||
|
Token::Float(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello_world() -> Result<(), TokenizerError> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
open(),
|
||||||
|
iden("print"),
|
||||||
|
string("Hello, World!"),
|
||||||
|
close()
|
||||||
|
],
|
||||||
|
quick_tokenize("
|
||||||
|
(print \"Hello, World!\")
|
||||||
|
")?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math() -> Result<(), TokenizerError> {
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
open(),
|
||||||
|
iden("print"),
|
||||||
|
open(),
|
||||||
|
iden("add"),
|
||||||
|
open(),
|
||||||
|
iden("div"),
|
||||||
|
float(4.5),
|
||||||
|
open(),
|
||||||
|
iden("sub"),
|
||||||
|
float(0.5),
|
||||||
|
float(0.2),
|
||||||
|
close(),
|
||||||
|
close(),
|
||||||
|
open(),
|
||||||
|
iden("mul"),
|
||||||
|
int(21),
|
||||||
|
float(0.05),
|
||||||
|
close(),
|
||||||
|
close(),
|
||||||
|
close()
|
||||||
|
],
|
||||||
|
quick_tokenize("
|
||||||
|
(print
|
||||||
|
(add
|
||||||
|
(div
|
||||||
|
4.5
|
||||||
|
(sub 0.5 0.2)
|
||||||
|
)
|
||||||
|
(mul 21 0.05)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
")?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Token {
|
||||||
|
OpenParen,
|
||||||
|
CloseParen,
|
||||||
|
Quote,
|
||||||
|
Unquote,
|
||||||
|
Identifier(String),
|
||||||
|
Index(String, String),
|
||||||
|
String(String),
|
||||||
|
Integer(i32),
|
||||||
|
Float(f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TokenizerError {
|
||||||
|
line: u64,
|
||||||
|
column: u64,
|
||||||
|
|
||||||
|
line_end: u64,
|
||||||
|
column_end: u64,
|
||||||
|
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TokenizerError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.line != self.line_end || self.column != self.column_end {
|
||||||
|
return write!(f, "Error from {}:{} to {}:{}, '{}'", self.line, self.column, self.line_end, self.column_end, self.message);
|
||||||
|
}
|
||||||
|
write!(f, "Error at {}:{}, '{}'", self.line, self.column, self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for TokenizerError {}
|
||||||
|
|
||||||
|
pub struct Tokenizer {
|
||||||
|
code: String,
|
||||||
|
line: u64,
|
||||||
|
column: u64,
|
||||||
|
|
||||||
|
reading_string: bool,
|
||||||
|
escape_next_char: bool,
|
||||||
|
|
||||||
|
reading_num: bool,
|
||||||
|
is_float: bool,
|
||||||
|
|
||||||
|
reading_identifier: bool,
|
||||||
|
is_index: bool,
|
||||||
|
indexing: Option<String>,
|
||||||
|
|
||||||
|
skipping_comment: bool,
|
||||||
|
|
||||||
|
storage: Vec<char>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tokenizer {
|
||||||
|
pub fn new(code: String) -> Tokenizer {
|
||||||
|
Self {
|
||||||
|
code,
|
||||||
|
line: 1,
|
||||||
|
column: 1,
|
||||||
|
|
||||||
|
reading_num: false,
|
||||||
|
is_float: false,
|
||||||
|
|
||||||
|
reading_string: false,
|
||||||
|
escape_next_char: false,
|
||||||
|
|
||||||
|
reading_identifier: false,
|
||||||
|
is_index: false,
|
||||||
|
indexing: None,
|
||||||
|
|
||||||
|
skipping_comment: false,
|
||||||
|
|
||||||
|
storage: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tokenize(&mut self) -> Result<Vec<Token>, TokenizerError> {
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
|
for char in self.code.chars() {
|
||||||
|
let line = self.line;
|
||||||
|
let column = self.column;
|
||||||
|
|
||||||
|
self.column += 1;
|
||||||
|
if char == '\n' {
|
||||||
|
self.line += 1;
|
||||||
|
self.column = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.skipping_comment {
|
||||||
|
match char {
|
||||||
|
'\n' => {
|
||||||
|
self.skipping_comment = false;
|
||||||
|
},
|
||||||
|
_ => continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.reading_identifier {
|
||||||
|
if char.is_alphabetic() {
|
||||||
|
self.storage.push(char);
|
||||||
|
continue;
|
||||||
|
} else if char == '.' {
|
||||||
|
self.is_index = true;
|
||||||
|
self.indexing = Some(self.storage.iter().collect());
|
||||||
|
self.storage.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reading_identifier = false;
|
||||||
|
|
||||||
|
if self.is_index {
|
||||||
|
self.is_index = false;
|
||||||
|
|
||||||
|
tokens.push(Token::Index(self.indexing.as_ref().unwrap().to_string(), self.storage.iter().collect()));
|
||||||
|
|
||||||
|
self.storage.clear();
|
||||||
|
self.indexing = None;
|
||||||
|
} else {
|
||||||
|
tokens.push(Token::Identifier(self.storage.iter().collect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.storage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.reading_num {
|
||||||
|
// Allow spacing numbers like 1_000_000
|
||||||
|
if !char.is_numeric() && char != '_' && char != '.' {
|
||||||
|
self.reading_num = false;
|
||||||
|
|
||||||
|
if self.is_float {
|
||||||
|
tokens.push(Token::Float(self.storage.iter().collect::<String>().parse().unwrap()));
|
||||||
|
} else {
|
||||||
|
tokens.push(Token::Integer(self.storage.iter().collect::<String>().parse().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.is_float = false;
|
||||||
|
|
||||||
|
self.storage.clear();
|
||||||
|
} else {
|
||||||
|
if char == '.' {
|
||||||
|
self.is_float = true;
|
||||||
|
}
|
||||||
|
if char != '_' {
|
||||||
|
self.storage.push(char);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.reading_string {
|
||||||
|
if char == '"' && !self.escape_next_char {
|
||||||
|
self.reading_string = false;
|
||||||
|
|
||||||
|
tokens.push(Token::String(self.storage.iter().collect()));
|
||||||
|
self.storage.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if char == '\n' {
|
||||||
|
return Err(TokenizerError {
|
||||||
|
line: line - self.storage.len() as u64 - 1,
|
||||||
|
column,
|
||||||
|
|
||||||
|
line_end: line,
|
||||||
|
column_end: column,
|
||||||
|
|
||||||
|
message: String::from("Expected \", got \\n"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if char == '\\' {
|
||||||
|
self.escape_next_char = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.storage.push(char);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match char {
|
||||||
|
';' => self.skipping_comment = true,
|
||||||
|
'(' => tokens.push(Token::OpenParen),
|
||||||
|
')' => tokens.push(Token::CloseParen),
|
||||||
|
'"' => {
|
||||||
|
self.reading_string = true;
|
||||||
|
self.storage.clear();
|
||||||
|
},
|
||||||
|
'\'' => tokens.push(Token::Quote),
|
||||||
|
',' => tokens.push(Token::Unquote),
|
||||||
|
c => {
|
||||||
|
if self.reading_identifier || self.reading_num || self.reading_string {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if c.is_alphabetic() {
|
||||||
|
self.reading_identifier = true;
|
||||||
|
self.storage.clear();
|
||||||
|
self.storage.push(c);
|
||||||
|
continue;
|
||||||
|
} else if c.is_numeric() || c == '.' {
|
||||||
|
self.reading_num = true;
|
||||||
|
self.storage.clear();
|
||||||
|
if c.is_numeric() {
|
||||||
|
self.storage.push(c);
|
||||||
|
} else if c == '.' {
|
||||||
|
self.is_float = true;
|
||||||
|
self.storage.push('0');
|
||||||
|
self.storage.push(c);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue