before better function hierarchy
This commit is contained in:
parent
dd5debe93e
commit
d856452c76
10 changed files with 229 additions and 81 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
|
@ -214,6 +214,7 @@ dependencies = [
|
||||||
"tower-livereload",
|
"tower-livereload",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1300,6 +1301,15 @@ version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.28"
|
version = "0.1.28"
|
||||||
|
|
@ -1980,6 +1990,16 @@ version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
@ -2128,6 +2148,15 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,4 @@ anyhow = "1.0"
|
||||||
tower-livereload = "0.9.6"
|
tower-livereload = "0.9.6"
|
||||||
log = "0.4.28"
|
log = "0.4.28"
|
||||||
bumpalo = { version = "3.19", features = ["collections", "boxed", "serde"] }
|
bumpalo = { version = "3.19", features = ["collections", "boxed", "serde"] }
|
||||||
|
walkdir = "2.5.0"
|
||||||
|
|
|
||||||
19
README.md
19
README.md
|
|
@ -1,9 +1,9 @@
|
||||||
# crud-lang
|
# crud-lang
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
1. Existing languages are great, but building web services is bolted on, instead of supported out-of-the-box.
|
1. Existing languages are just fine, but building web services is bolted on, instead of supported out-of-the-box.
|
||||||
2. Whereas every company needs an API these days.
|
2. Whereas every company needs an API these days.
|
||||||
3. I always have trouble mapping urls the code that handles them.
|
3. Is it just me? -I always have trouble mapping urls the code that handles them.
|
||||||
4. There is no language (AFAIK) that supports layering. (controllers, services, database access, etc). This pattern is ubiquitous (at least where I live).
|
4. There is no language (AFAIK) that supports layering. (controllers, services, database access, etc). This pattern is ubiquitous (at least where I live).
|
||||||
5. ORM's are awful. Mapping from sql rows to objects is a pain. This should be easy.
|
5. ORM's are awful. Mapping from sql rows to objects is a pain. This should be easy.
|
||||||
6. Json is ubiquitous. Convention over configuration: A controller returns json by default.
|
6. Json is ubiquitous. Convention over configuration: A controller returns json by default.
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
### An interpreter written in Rust.
|
### An interpreter written in Rust.
|
||||||
OMG!
|
OMG!
|
||||||
And it has everything I like in other languages
|
And I cherry picked things I like, mostly from rust and python.
|
||||||
- strictly typed
|
- strictly typed
|
||||||
- [] is a list
|
- [] is a list
|
||||||
- {} is a map
|
- {} is a map
|
||||||
|
|
@ -41,7 +41,7 @@ And it has everything I like in other languages
|
||||||
- structs and duck typing
|
- structs and duck typing
|
||||||
- everything is an expression
|
- everything is an expression
|
||||||
- nice iterators.
|
- nice iterators.
|
||||||
- First class functions? Maybe...
|
- First-class functions? Maybe...
|
||||||
- automatic mapping from database to object to json
|
- automatic mapping from database to object to json
|
||||||
- indenting like python
|
- indenting like python
|
||||||
|
|
||||||
|
|
@ -63,12 +63,11 @@ And it has everything I like in other languages
|
||||||
- a very simple api that listens to GET /api/customers{:id} and returns a customer from the database
|
- a very simple api that listens to GET /api/customers{:id} and returns a customer from the database
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
* heavily inspired by Crafting Interpreters
|
* heavily inspired by Crafting Interpreters.
|
||||||
* language influences from rust and python
|
|
||||||
* compiler first creates an AST and then compiles to bytecode (no file format yet)
|
* compiler first creates an AST and then compiles to bytecode (no file format yet)
|
||||||
* uses a stack-based virtual machine
|
* uses a stack-based virtual machine
|
||||||
|
|
||||||
## Current status:
|
## Current status: infancy
|
||||||
* compiler and runtime are still limited but working
|
* compiler and runtime are still limited but working
|
||||||
* supports:
|
* supports:
|
||||||
* basic types:
|
* basic types:
|
||||||
|
|
@ -91,6 +90,12 @@ And it has everything I like in other languages
|
||||||
- control flow
|
- control flow
|
||||||
- tests
|
- tests
|
||||||
|
|
||||||
|
## What about performance?
|
||||||
|
* Clueless really! We'll see.
|
||||||
|
* But it is written in rust
|
||||||
|
* And it has no GC
|
||||||
|
* So, maybe it will compete with python?
|
||||||
|
|
||||||
## A quick taste
|
## A quick taste
|
||||||
**variables**
|
**variables**
|
||||||
```
|
```
|
||||||
|
|
|
||||||
5
source/hello/web.crud
Normal file
5
source/hello/web.crud
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn get() -> string:
|
||||||
|
add("hello", "world")
|
||||||
|
|
||||||
|
fn add(a: string, b: string) -> string:
|
||||||
|
a + b
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
use crate::tokens::TokenType::{Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, If, Indent, Integer, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64, RightBracket};
|
use crate::tokens::TokenType::{
|
||||||
|
Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater,
|
||||||
|
GreaterEqual, GreaterGreater, I32, I64, Identifier, If, Indent, Integer, LeftBracket,
|
||||||
|
LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print,
|
||||||
|
RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64,
|
||||||
|
};
|
||||||
use crate::tokens::{Token, TokenType};
|
use crate::tokens::{Token, TokenType};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
@ -6,7 +11,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
||||||
let mut compiler = AstCompiler::new(tokens);
|
let mut compiler = AstCompiler::new(tokens);
|
||||||
compiler.compile(0)
|
compiler.compile_tokens(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -38,17 +43,84 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(&mut self, expected_indent: usize) -> anyhow::Result<Vec<Statement>> {
|
fn reset(&mut self) {
|
||||||
let mut statements = vec![];
|
self.current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_tokens(&mut self, expected_indent: usize) -> anyhow::Result<Vec<Statement>> {
|
||||||
|
self.collect_functions()?;
|
||||||
|
self.reset();
|
||||||
|
self.compile(expected_indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile(&mut self, expected_indent: usize) -> anyhow::Result<Vec<Statement>>{
|
||||||
|
if !self.had_error {
|
||||||
|
let mut statements = vec![];
|
||||||
|
while !self.is_at_end() {
|
||||||
|
let statement = self.indent(expected_indent)?;
|
||||||
|
if let Some(statement) = statement {
|
||||||
|
statements.push(statement);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(statements)
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Compilation failed."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_functions(&mut self) -> anyhow::Result<()> {
|
||||||
while !self.is_at_end() {
|
while !self.is_at_end() {
|
||||||
let statement = self.indent(expected_indent)?;
|
if self.match_token(vec![Fn]) {
|
||||||
if let Some(statement) = statement {
|
let name_token = self.consume(Identifier, "Expect function name.")?;
|
||||||
statements.push(statement);
|
self.consume(LeftParen, "Expect '(' after function name.")?;
|
||||||
|
let mut parameters = vec![];
|
||||||
|
while !self.check(RightParen) {
|
||||||
|
if parameters.len() >= 25 {
|
||||||
|
return Err(anyhow::anyhow!("Too many parameters."));
|
||||||
|
}
|
||||||
|
let parm_name = self.consume(Identifier, "Expect parameter name.")?;
|
||||||
|
|
||||||
|
self.consume(Colon, "Expect : after parameter name")?;
|
||||||
|
let var_type = self.peek().token_type;
|
||||||
|
self.vars.push(Expression::Variable {
|
||||||
|
name: parm_name.lexeme.to_string(),
|
||||||
|
var_type,
|
||||||
|
line: parm_name.line,
|
||||||
|
});
|
||||||
|
self.advance();
|
||||||
|
parameters.push(Parameter {
|
||||||
|
name: parm_name,
|
||||||
|
var_type,
|
||||||
|
});
|
||||||
|
if self.peek().token_type == TokenType::Comma {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.consume(RightParen, "Expect ')' after parameters.")?;
|
||||||
|
let return_type = if self.check(SingleRightArrow) {
|
||||||
|
self.consume(SingleRightArrow, "")?;
|
||||||
|
self.advance().token_type
|
||||||
|
} else {
|
||||||
|
TokenType::Void
|
||||||
|
};
|
||||||
|
self.consume(Colon, "Expect colon (:) after function declaration.")?;
|
||||||
|
self.consume(Eol, "Expect end of line.")?;
|
||||||
|
|
||||||
|
let function = Function {
|
||||||
|
name: name_token.clone(),
|
||||||
|
parameters,
|
||||||
|
return_type,
|
||||||
|
body: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
self.functions.insert(name_token.lexeme, function);
|
||||||
} else {
|
} else {
|
||||||
break;
|
self.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(statements)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indent(&mut self, expected_indent: usize) -> anyhow::Result<Option<Statement>> {
|
fn indent(&mut self, expected_indent: usize) -> anyhow::Result<Option<Statement>> {
|
||||||
|
|
@ -89,50 +161,25 @@ impl AstCompiler {
|
||||||
fn function_declaration(&mut self) -> anyhow::Result<Statement> {
|
fn function_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||||
let name_token = self.consume(Identifier, "Expect function name.")?;
|
let name_token = self.consume(Identifier, "Expect function name.")?;
|
||||||
self.consume(LeftParen, "Expect '(' after function name.")?;
|
self.consume(LeftParen, "Expect '(' after function name.")?;
|
||||||
let mut parameters = vec![];
|
|
||||||
while !self.check(RightParen) {
|
while !self.check(RightParen) {
|
||||||
if parameters.len() >= 25 {
|
|
||||||
return Err(anyhow::anyhow!("Too many parameters."));
|
|
||||||
}
|
|
||||||
let parm_name = self.consume(Identifier, "Expect parameter name.")?;
|
|
||||||
|
|
||||||
self.consume(Colon, "Expect : after parameter name")?;
|
|
||||||
let var_type = self.peek().token_type;
|
|
||||||
self.vars.push(Expression::Variable {
|
|
||||||
name: parm_name.lexeme.to_string(),
|
|
||||||
var_type,
|
|
||||||
line: parm_name.line,
|
|
||||||
});
|
|
||||||
self.advance();
|
self.advance();
|
||||||
parameters.push(Parameter {
|
|
||||||
name: parm_name,
|
|
||||||
var_type,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.consume(RightParen, "Expect ')' after parameters.")?;
|
self.consume(RightParen, "Expect ')' after parameters.")?;
|
||||||
let return_type = if self.check(SingleRightArrow) {
|
while !self.check(Colon) {
|
||||||
self.consume(SingleRightArrow, "")?;
|
self.advance();
|
||||||
self.advance().token_type
|
}
|
||||||
} else {
|
self.consume(Colon, "2Expect colon (:) after function declaration.")?;
|
||||||
TokenType::Void
|
|
||||||
};
|
|
||||||
self.consume(Colon, "Expect colon (:) after function declaration.")?;
|
|
||||||
self.consume(Eol, "Expect end of line.")?;
|
self.consume(Eol, "Expect end of line.")?;
|
||||||
|
|
||||||
let current_indent = self.indent.last().unwrap();
|
let current_indent = self.indent.last().unwrap();
|
||||||
let body = self.compile(current_indent + 1)?;
|
let body = self.compile(current_indent + 1)?;
|
||||||
|
|
||||||
let function = Function {
|
self.functions.get_mut(&name_token.lexeme).unwrap().body = body;
|
||||||
name: name_token.clone(),
|
|
||||||
parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
};
|
|
||||||
|
|
||||||
let function_stmt = Statement::FunctionStmt {
|
let function_stmt = Statement::FunctionStmt {
|
||||||
function: function.clone(),
|
function: self.functions.get(&name_token.lexeme).unwrap().clone(),
|
||||||
};
|
};
|
||||||
self.functions.insert(name_token.lexeme, function.clone());
|
|
||||||
Ok(function_stmt)
|
Ok(function_stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,7 +381,7 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn list(&mut self) -> anyhow::Result<Expression> {
|
fn list(&mut self) -> anyhow::Result<Expression> {
|
||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
while !self.match_token(vec![RightBracket]){
|
while !self.match_token(vec![RightBracket]) {
|
||||||
list.push(self.expression()?);
|
list.push(self.expression()?);
|
||||||
if self.peek().token_type == TokenType::Comma {
|
if self.peek().token_type == TokenType::Comma {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
@ -344,8 +391,10 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Expression::List {
|
Ok(Expression::List {
|
||||||
values: list, literaltype: ListType, line: self.peek().line},
|
values: list,
|
||||||
)
|
literaltype: ListType,
|
||||||
|
line: self.peek().line,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> {
|
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> {
|
||||||
|
|
@ -369,6 +418,7 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> {
|
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> {
|
||||||
|
println!("function call {}", name);
|
||||||
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
|
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
|
||||||
let function = self.functions.get(&function_name).unwrap().clone();
|
let function = self.functions.get(&function_name).unwrap().clone();
|
||||||
|
|
||||||
|
|
@ -408,7 +458,11 @@ impl AstCompiler {
|
||||||
self.advance();
|
self.advance();
|
||||||
} else {
|
} else {
|
||||||
self.had_error = true;
|
self.had_error = true;
|
||||||
return Err(anyhow::anyhow!(message.to_string()));
|
return Err(anyhow::anyhow!(
|
||||||
|
"{} at {:?}",
|
||||||
|
message.to_string(),
|
||||||
|
self.peek()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(self.previous().clone())
|
Ok(self.previous().clone())
|
||||||
}
|
}
|
||||||
|
|
@ -640,7 +694,7 @@ impl Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Grouping { expression,.. } => expression.infer_type(),
|
Self::Grouping { expression, .. } => expression.infer_type(),
|
||||||
Self::Literal { literaltype, .. } => literaltype.clone(),
|
Self::Literal { literaltype, .. } => literaltype.clone(),
|
||||||
Self::List { literaltype, .. } => literaltype.clone(),
|
Self::List { literaltype, .. } => literaltype.clone(),
|
||||||
Self::Unary { right, .. } => right.infer_type(),
|
Self::Unary { right, .. } => right.infer_type(),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::vm::{
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn compile(ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
pub fn compile(ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||||
compile_name(ast, "_")
|
compile_name(ast, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_function(function: &Function) -> anyhow::Result<Chunk> {
|
pub(crate) fn compile_function(function: &Function) -> anyhow::Result<Chunk> {
|
||||||
|
|
@ -98,6 +98,7 @@ impl Compiler {
|
||||||
Expression::FunctionCall {
|
Expression::FunctionCall {
|
||||||
name, arguments, ..
|
name, arguments, ..
|
||||||
} => {
|
} => {
|
||||||
|
println!("call {}",name);
|
||||||
let function_index = *self.functions.get(name).unwrap();
|
let function_index = *self.functions.get(name).unwrap();
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
self.compile_expression(argument)?;
|
self.compile_expression(argument)?;
|
||||||
|
|
|
||||||
13
src/chunk.rs
13
src/chunk.rs
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
|
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
|
||||||
|
|
@ -6,12 +7,14 @@ use crate::vm::{
|
||||||
OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
name: String,
|
name: String,
|
||||||
pub code: Vec<u16>,
|
pub code: Vec<u16>,
|
||||||
pub constants: Vec<Value>,
|
pub constants: Vec<Value>,
|
||||||
lines: Vec<usize>,
|
lines: Vec<usize>,
|
||||||
pub(crate) functions: Vec<Chunk>,
|
pub functions: HashMap<String, Chunk>,
|
||||||
|
pub(crate) functions_by_index: Vec<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
|
|
@ -21,7 +24,8 @@ impl Chunk {
|
||||||
code: Vec::new(),
|
code: Vec::new(),
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
functions: Vec::new(),
|
functions: HashMap::new(),
|
||||||
|
functions_by_index: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,12 +40,13 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_function(&mut self, function: Chunk) -> usize {
|
pub fn add_function(&mut self, function: Chunk) -> usize {
|
||||||
self.functions.push(function);
|
self.functions_by_index.push(function.clone());
|
||||||
|
self.functions.insert(function.name.to_string(), function);
|
||||||
self.functions.len() - 1
|
self.functions.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) {
|
pub fn disassemble(&self) {
|
||||||
for f in &self.functions {
|
for f in self.functions.values() {
|
||||||
f.disassemble();
|
f.disassemble();
|
||||||
}
|
}
|
||||||
println!("== {} ==", self.name);
|
println!("== {} ==", self.name);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ pub mod scanner;
|
||||||
pub mod ast_compiler;
|
pub mod ast_compiler;
|
||||||
pub mod bytecode_compiler;
|
pub mod bytecode_compiler;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
mod chunk;
|
pub mod chunk;
|
||||||
mod keywords;
|
mod keywords;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
|
||||||
88
src/main.rs
88
src/main.rs
|
|
@ -1,31 +1,79 @@
|
||||||
|
use axum::extract::{Path, State};
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::routing::get;
|
||||||
|
use axum::{Json, Router};
|
||||||
use crudlang::ast_compiler;
|
use crudlang::ast_compiler;
|
||||||
use crudlang::bytecode_compiler::compile;
|
use crudlang::bytecode_compiler::compile;
|
||||||
|
use crudlang::chunk::Chunk;
|
||||||
use crudlang::scanner::scan;
|
use crudlang::scanner::scan;
|
||||||
use crudlang::vm::interpret;
|
use crudlang::vm::interpret;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let tokens = scan(
|
let mut paths = HashMap::new();
|
||||||
r#"
|
for entry in WalkDir::new("source").into_iter().filter_map(|e| e.ok()) {
|
||||||
fn main(a: list) -> list:
|
let path = entry.path();
|
||||||
a + 42
|
if path.is_file() && path.ends_with("web.crud") {
|
||||||
|
print!("compiling {:?}: ", path);
|
||||||
let text:list = ["hello "]
|
let source = fs::read_to_string(path)?;
|
||||||
main(text)"#,
|
let tokens = scan(&source);
|
||||||
);
|
match ast_compiler::compile(tokens) {
|
||||||
println!("{:?}", tokens);
|
Ok(statements) => {
|
||||||
match ast_compiler::compile(tokens) {
|
let chunk = compile(&statements)?;
|
||||||
Ok(statements) => {
|
let path = path.strip_prefix("source")?.to_str().unwrap();
|
||||||
println!("{:?}", statements);
|
let path = path.replace("/web.crud", "");
|
||||||
let chunk = compile(&statements)?;
|
paths.insert(format!("/{}", path), chunk);
|
||||||
chunk.disassemble();
|
}
|
||||||
println!("{}",interpret(&chunk)?);
|
Err(e) => {
|
||||||
}
|
println!("{}", e);
|
||||||
Err(e) => {
|
break;
|
||||||
println!("{}", e)
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !paths.is_empty() {
|
||||||
|
let mut app = Router::new();
|
||||||
|
for (path, code) in paths.iter() {
|
||||||
|
let code = code.functions.get("get").unwrap();
|
||||||
|
let state = Arc::new(AppState { code: code.clone() });
|
||||||
|
println!("adding {}", path);
|
||||||
|
app = app.route(path, get(handle_get).with_state(state.clone()));
|
||||||
|
// .with_state(state);
|
||||||
|
}
|
||||||
|
// run our app with hyper, listening globally on port 3000
|
||||||
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||||
|
println!("listening on {}", listener.local_addr()?);
|
||||||
|
axum::serve(listener, app).await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct AppState {
|
||||||
|
code: Chunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, StatusCode> {
|
||||||
|
Ok(Json(interpret(&state.code).await.unwrap().to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// let tokens = scan("");
|
||||||
|
//
|
||||||
|
// match ast_compiler::compile(tokens) {
|
||||||
|
// Ok(statements) => {
|
||||||
|
// println!("{:?}", statements);
|
||||||
|
// let chunk = compile(&statements)?;
|
||||||
|
// chunk.disassemble();
|
||||||
|
// println!("{}",interpret(&chunk)?);
|
||||||
|
// }
|
||||||
|
// Err(e) => {
|
||||||
|
// println!("{}", e)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub struct Vm {
|
||||||
arena: Bump,
|
arena: Bump,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
pub async fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
||||||
let mut vm = Vm {
|
let mut vm = Vm {
|
||||||
ip: 0,
|
ip: 0,
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
|
|
@ -145,7 +145,7 @@ impl Vm {
|
||||||
}
|
}
|
||||||
OP_CALL => {
|
OP_CALL => {
|
||||||
let function_index = self.read(chunk);
|
let function_index = self.read(chunk);
|
||||||
let function = chunk.functions.get(function_index).unwrap();
|
let function = chunk.functions_by_index.get(function_index).unwrap();
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
let num_args = self.read(chunk);
|
let num_args = self.read(chunk);
|
||||||
for _ in 0..num_args {
|
for _ in 0..num_args {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue