basic parser and interpreter from example
This commit is contained in:
commit
a587f1be24
6 changed files with 531 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
285
Cargo.lock
generated
Normal file
285
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cd3ef0a728f561e3b4157213d178ae7523cbc405423f862da757447588ae103"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
"stacker",
|
||||
"unicode-ident",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e66fcd288453b748497d8fb18bccc83a16b0518e3906d4b8df0a8d42d93dbb1c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "jang"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.11.1"
|
||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
this is a programming language maybe
|
||||
231
src/main.rs
Normal file
231
src/main.rs
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use chumsky::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Expr<'src> {
|
||||
Num(f64),
|
||||
Var(&'src str),
|
||||
|
||||
Neg(Box<Expr<'src>>),
|
||||
Add(Box<Expr<'src>>, Box<Expr<'src>>),
|
||||
Sub(Box<Expr<'src>>, Box<Expr<'src>>),
|
||||
Mul(Box<Expr<'src>>, Box<Expr<'src>>),
|
||||
Div(Box<Expr<'src>>, Box<Expr<'src>>),
|
||||
|
||||
Call(&'src str, Vec<Expr<'src>>),
|
||||
Let {
|
||||
name: &'src str,
|
||||
rhs: Box<Expr<'src>>,
|
||||
then: Box<Expr<'src>>,
|
||||
},
|
||||
Fn {
|
||||
name: &'src str,
|
||||
args: Vec<&'src str>,
|
||||
body: Rc<Expr<'src>>,
|
||||
then: Box<Expr<'src>>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let path = std::env::args().nth(1).unwrap();
|
||||
let src = std::fs::read_to_string(&path).unwrap();
|
||||
println!("Parsing file at {path}");
|
||||
println!("src:\n{}\n---", src);
|
||||
let parse_result = create_parser().parse(&src);
|
||||
println!("parsed:\n{:?}\n---", parse_result);
|
||||
if let Ok(ast) = parse_result.into_result() {
|
||||
let mut vars = HashMap::new();
|
||||
let output = eval(&ast, &mut vars);
|
||||
println!("{:?}", output);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum IdentValue<'src> {
|
||||
Var(f64),
|
||||
Func {
|
||||
name: &'src str,
|
||||
params: Vec<&'src str>,
|
||||
body: Rc<Expr<'src>>,
|
||||
},
|
||||
}
|
||||
|
||||
fn eval<'src>(
|
||||
expr: &Expr<'src>,
|
||||
vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>,
|
||||
) -> Result<f64, String> {
|
||||
fn bind_var<'src>(
|
||||
vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>,
|
||||
name: &'src str,
|
||||
val: IdentValue<'src>,
|
||||
) {
|
||||
vars.entry(name).or_insert(Vec::new()).push(val);
|
||||
}
|
||||
fn unbind_var<'src>(vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>, name: &'src str) {
|
||||
vars.get_mut(name).unwrap().pop();
|
||||
}
|
||||
match expr {
|
||||
Expr::Num(x) => Ok(*x),
|
||||
Expr::Neg(a) => Ok(-eval(a, vars)?),
|
||||
Expr::Add(a, b) => Ok(eval(a, vars)? + eval(b, vars)?),
|
||||
Expr::Sub(a, b) => Ok(eval(a, vars)? - eval(b, vars)?),
|
||||
Expr::Mul(a, b) => Ok(eval(a, vars)? * eval(b, vars)?),
|
||||
Expr::Div(a, b) => Ok(eval(a, vars)? / eval(b, vars)?),
|
||||
Expr::Var(name) => {
|
||||
let ident_value = vars.get(name).and_then(|v| v.last());
|
||||
match ident_value {
|
||||
Some(IdentValue::Var(v)) => Ok(*v),
|
||||
Some(IdentValue::Func { .. }) => Err("Expected function to be called".to_string()),
|
||||
None => Err(format!("Undefined variable `{}`", name)),
|
||||
}
|
||||
}
|
||||
Expr::Let { name, rhs, then } => {
|
||||
let val = eval(rhs, vars)?;
|
||||
bind_var(vars, name, IdentValue::Var(val));
|
||||
let output = eval(then, vars);
|
||||
unbind_var(vars, name);
|
||||
output
|
||||
}
|
||||
Expr::Call(name, args) => {
|
||||
let ident_value = vars.get(name).and_then(|v| v.last()).cloned();
|
||||
match ident_value {
|
||||
Some(IdentValue::Func { name, params, body }) => {
|
||||
if params.len() != args.len() {
|
||||
Err(format!(
|
||||
"expected {} arguments but only {} were provided to {}",
|
||||
params.len(),
|
||||
args.len(),
|
||||
name
|
||||
))
|
||||
} else {
|
||||
let binds = params
|
||||
.iter()
|
||||
.zip(args)
|
||||
.map(|(param, arg)| (param, eval(arg, vars)))
|
||||
.collect::<Vec<_>>();
|
||||
for (param, bind) in binds {
|
||||
bind_var(vars, param, IdentValue::Var(bind.unwrap()));
|
||||
}
|
||||
let return_value = eval(&body, vars);
|
||||
for param in params.iter() {
|
||||
unbind_var(vars, param);
|
||||
}
|
||||
return_value
|
||||
}
|
||||
}
|
||||
Some(IdentValue::Var(_)) => {
|
||||
Err("Variable cannot be called like a function".to_string())
|
||||
}
|
||||
None => Err(format!("Undefined variable `{}`", name)),
|
||||
}
|
||||
}
|
||||
Expr::Fn {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
then,
|
||||
} => {
|
||||
vars.entry(name)
|
||||
.or_insert(Vec::new())
|
||||
.push(IdentValue::Func {
|
||||
name,
|
||||
params: args.clone(),
|
||||
body: body.clone(),
|
||||
});
|
||||
let output = eval(then, vars);
|
||||
vars.get_mut(name).unwrap().pop();
|
||||
output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_parser<'src>() -> impl Parser<'src, &'src str, Expr<'src>> {
|
||||
let ident = text::ascii::ident().padded();
|
||||
|
||||
let expr = recursive(|expr| {
|
||||
let int = text::int(10)
|
||||
.map(|s: &str| Expr::Num(s.parse().unwrap()))
|
||||
.padded();
|
||||
|
||||
let call = ident
|
||||
.then(
|
||||
expr.clone()
|
||||
.separated_by(just(','))
|
||||
.allow_trailing()
|
||||
.collect::<Vec<_>>()
|
||||
.delimited_by(just('('), just(')')),
|
||||
)
|
||||
.map(|(f, args)| Expr::Call(f, args));
|
||||
|
||||
let atom = int
|
||||
.or(expr.delimited_by(just('('), just(')')))
|
||||
.or(call)
|
||||
.or(ident.map(Expr::Var))
|
||||
.padded();
|
||||
|
||||
let op = |c| just(c).padded();
|
||||
|
||||
let unary = op('-')
|
||||
.repeated()
|
||||
.foldr(atom, |_op, rhs| Expr::Neg(Box::new(rhs)));
|
||||
|
||||
let product = unary.clone().foldl(
|
||||
choice((
|
||||
op('*').to(Expr::Mul as fn(_, _) -> _),
|
||||
op('/').to(Expr::Div as fn(_, _) -> _),
|
||||
))
|
||||
.then(unary)
|
||||
.repeated(),
|
||||
|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)),
|
||||
);
|
||||
|
||||
let sum = product.clone().foldl(
|
||||
choice((
|
||||
op('+').to(Expr::Add as fn(_, _) -> _),
|
||||
op('-').to(Expr::Sub as fn(_, _) -> _),
|
||||
))
|
||||
.then(product)
|
||||
.repeated(),
|
||||
|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)),
|
||||
);
|
||||
|
||||
sum
|
||||
});
|
||||
|
||||
let decl = recursive(|decl| {
|
||||
// ex: let x = 5*2
|
||||
let decl_let = text::ascii::keyword("let")
|
||||
.ignore_then(ident)
|
||||
.then_ignore(just('='))
|
||||
.then(expr.clone())
|
||||
.then_ignore(just(';'))
|
||||
.then(decl.clone())
|
||||
.map(|((name, rhs), then)| Expr::Let {
|
||||
name,
|
||||
rhs: Box::new(rhs),
|
||||
then: Box::new(then),
|
||||
});
|
||||
|
||||
// ex: fn f x y = x + y
|
||||
let decl_fn = text::ascii::keyword("fn")
|
||||
.ignore_then(ident)
|
||||
// collect params
|
||||
.then(ident.repeated().collect::<Vec<_>>())
|
||||
.then_ignore(just('='))
|
||||
.then(expr.clone())
|
||||
.then_ignore(just(';'))
|
||||
.then(decl)
|
||||
.map(|(((name, params), body), then)| Expr::Fn {
|
||||
name,
|
||||
args: params,
|
||||
body: Rc::new(body),
|
||||
then: Box::new(then),
|
||||
});
|
||||
|
||||
// must parse 'let' keyword first so it doesn't become and identifier
|
||||
decl_fn.or(decl_let).or(expr).padded()
|
||||
});
|
||||
|
||||
decl
|
||||
}
|
||||
6
test.jang
Normal file
6
test.jang
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
let five = 5;
|
||||
let five = five + 3;
|
||||
fn sum x y = x + y;
|
||||
fn mul x y = x*y;
|
||||
let six = sum(1,sum(mul(2,2),3));
|
||||
five * 3 + six
|
||||
Loading…
Add table
Add a link
Reference in a new issue