authorchloe caruso <git@paperclover.net> 2024-01-20 09:38:23-08:00
committerchloe caruso <git@paperclover.net> 2024-01-20 09:38:23-08:00
log9593137d0c6b96fae79c7366d2c37b6ddcd36d08
tree519ae1b9858dfd847159429ba8b72eadf0089d96
parent091dd237e9ee9de0d9e8f3b7a95ccbcffb618b68

swag


25 files changed, 437 insertions(+), 104 deletions(-)

.gitignore+1
...@@ -1 +1,2 @@...@@ -1 +1,2 @@
1target1target
2*.js
Cargo.lock+12
...@@ -126,6 +126,17 @@ version = "1.0.0"...@@ -126,6 +126,17 @@ version = "1.0.0"
126source = "registry+https://github.com/rust-lang/crates.io-index"126source = "registry+https://github.com/rust-lang/crates.io-index"
127checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"127checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
128128
129[[package]]
130name = "checker"
131version = "0.1.0"
132dependencies = [
133 "ast",
134 "miette",
135 "num",
136 "thiserror",
137 "util",
138]
139
129[[package]]140[[package]]
130name = "clap"141name = "clap"
131version = "4.4.14"142version = "4.4.14"
...@@ -171,6 +182,7 @@ name = "cli"...@@ -171,6 +182,7 @@ name = "cli"
171version = "0.1.0"182version = "0.1.0"
172dependencies = [183dependencies = [
173 "ast",184 "ast",
185 "checker",
174 "clap",186 "clap",
175 "js_ast",187 "js_ast",
176 "js_printer",188 "js_printer",
crates/ast/src/error.rs+4
...@@ -14,4 +14,8 @@ impl AstError {...@@ -14,4 +14,8 @@ impl AstError {
14 error: String::from(error),14 error: String::from(error),
15 }15 }
16 }16 }
17
18 pub fn from_string(error: String) -> AstError {
19 AstError { error }
20 }
17}21}
crates/ast/src/lib.rs+3-3
...@@ -13,9 +13,9 @@ use tokenizer::Token;...@@ -13,9 +13,9 @@ use tokenizer::Token;
13use parse_statement::parse_statement;13use parse_statement::parse_statement;
14use types::ParserContext;14use types::ParserContext;
1515
16pub use types::{Ast, Block, Type};16pub use types::{Ast, Block, Module, Type};
1717
18pub fn parse(tokens: Vec<Token>) -> Result<Vec<Ast>> {18pub fn parse(tokens: Vec<Token>) -> Result<Module> {
19 let mut ctx = ParserContext::new(tokens);19 let mut ctx = ParserContext::new(tokens);
20 let mut ast: Vec<Ast> = Vec::new();20 let mut ast: Vec<Ast> = Vec::new();
2121
...@@ -23,5 +23,5 @@ pub fn parse(tokens: Vec<Token>) -> Result<Vec<Ast>> {...@@ -23,5 +23,5 @@ pub fn parse(tokens: Vec<Token>) -> Result<Vec<Ast>> {
23 ast.push(parse_statement(&mut ctx)?);23 ast.push(parse_statement(&mut ctx)?);
24 }24 }
2525
26 Ok(ast)26 Ok(Module { value: ast })
27}27}
crates/ast/src/parse_statement.rs+17-33
...@@ -2,6 +2,8 @@ use miette::Result;...@@ -2,6 +2,8 @@ use miette::Result;
22
3use tokenizer::Token;3use tokenizer::Token;
44
5use crate::Type;
6
5use super::error::AstError;7use super::error::AstError;
6use super::parse_block::parse_block;8use super::parse_block::parse_block;
7use super::parse_expression::parse_expression;9use super::parse_expression::parse_expression;
...@@ -38,41 +40,22 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -38,41 +40,22 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
3840
39 return Ok(Ast::Public(Box::new(inner)));41 return Ok(Ast::Public(Box::new(inner)));
40 }42 }
41 Token::Let => {43 Token::Var | Token::Let => {
42 let name = match tokens.next() {44 let is_constant = match token {
43 Some(Token::Identifier(name)) => name,45 Token::Var => false,
44 _ => return Err(AstError::new("Expected identifier after var").into()),46 Token::Let => true,
45 };47 _ => unreachable!(),
46 let return_type = match tokens.next() {
47 Some(Token::Colon) => parse_type(tokens)?,
48 _ => return Err(AstError::new("Expected colon after identifier").into()),
49 };
50 let value = match tokens.next() {
51 Some(Token::Equals) => parse_expression(tokens)?,
52 _ => return Err(AstError::new("Expected equals after identifier").into()),
53 };
54 match tokens.next() {
55 Some(Token::Semicolon) => (),
56 _ => {
57 return Err(
58 AstError::new("Expected semicolon after constant declaration").into(),
59 )
60 }
61 };48 };
62 Ok(Ast::ConstantDeclaration {
63 name: name.clone(),
64 return_type,
65 value: Box::new(value),
66 })
67 }
68 Token::Var => {
69 let name = match tokens.next() {49 let name = match tokens.next() {
70 Some(Token::Identifier(name)) => name,50 Some(Token::Identifier(name)) => name,
71 _ => return Err(AstError::new("Expected identifier after var").into()),51 _ => return Err(AstError::new("Expected identifier after var").into()),
72 };52 };
73 let return_type = match tokens.next() {53 let return_type = match tokens.peek() {
74 Some(Token::Colon) => parse_type(tokens)?,54 Some(Token::Colon) => {
75 _ => return Err(AstError::new("Expected colon after identifier").into()),55 tokens.next();
56 parse_type(tokens)?
57 }
58 _ => Type::Infer,
76 };59 };
77 let value = match tokens.next() {60 let value = match tokens.next() {
78 Some(Token::Equals) => parse_expression(tokens)?,61 Some(Token::Equals) => parse_expression(tokens)?,
...@@ -82,14 +65,15 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {...@@ -82,14 +65,15 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> {
82 Some(Token::Semicolon) => (),65 Some(Token::Semicolon) => (),
83 _ => {66 _ => {
84 return Err(67 return Err(
85 AstError::new("Expected semicolon after variable declaration").into(),68 AstError::new("Expected semicolon after constant declaration").into(),
86 )69 )
87 }70 }
88 };71 };
89 Ok(Ast::VariableDeclaration {72 Ok(Ast::Declaration {
90 name: name.clone(),73 name: name.clone(),
91 return_type,74 decl_type: return_type,
92 value: Box::new(value),75 value: Box::new(value),
76 is_constant,
93 })77 })
94 }78 }
95 Token::Return => {79 Token::Return => {
crates/ast/src/parse_type.rs+11-4
...@@ -10,12 +10,19 @@ pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> {...@@ -10,12 +10,19 @@ pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> {
10 Some(Token::Identifier(typ)) => match typ.as_str() {10 Some(Token::Identifier(typ)) => match typ.as_str() {
11 "int" => Ok(Type::Int),11 "int" => Ok(Type::Int),
12 "float" => Ok(Type::Float),12 "float" => Ok(Type::Float),
13 "bigint" => Ok(Type::Bigint),13 "bigint" => Ok(Type::BigInt),
14 "bool" => Ok(Type::Bool),14 "bool" => Ok(Type::Bool),
15 "string" => Ok(Type::String),15 "string" => Ok(Type::String),
16 "any" => Ok(Type::Any),16 _ => {
17 _ => return Err(AstError::new("Expected type").into()),17 return Err(
18 AstError::from_string(format!(
19 "Invalid type '{}' (TODO: add custom type support)",
20 typ
21 )
22 ).into())
23 }
18 },24 },
19 _ => return Err(AstError::new("Expected type").into()),25 Some(unexpected) => return Err(AstError::from_string(format!("Expected type, got {}", unexpected)).into()),
26 None => return Err(AstError::from_string("Expected type, got EOF".to_string()).into()),
20 }27 }
21}28}
crates/ast/src/types.rs+54-10
...@@ -41,14 +41,62 @@ impl ParserContext {...@@ -41,14 +41,62 @@ impl ParserContext {
41 }41 }
42}42}
4343
44#[derive(Debug, Clone)]44#[derive(Debug, Clone, PartialEq)]
45pub enum Type {45pub enum Type {
46 /// Variables without an explicit type are assigned this until
47 /// the type inferencer figures out what this is. There is not a
48 /// keyword for this.
49 Infer,
50 /// The type assigned to numeric literals. This allows integer literals
51 /// to coerce to integer or float depending on the context, but prevents
52 /// implicit casting of runtime values.
53 NumberLiteral(f64),
54 /// Return value of `@raw`. All type checks are disabled.
55 Any,
56 /// noreturn is the type of 'return', aka a value that never exists
57 NoReturn,
58 /// 32 signed integer
46 Int,59 Int,
60 /// 64 signed JS float
47 Float,61 Float,
48 Bigint,62 /// Boolean
49 Bool,63 Bool,
64 /// JS BigInt
65 BigInt,
66 /// JS String
50 String,67 String,
51 Any,68 /// A union of multiple possible types. Might be removed
69 Union(Vec<Type>),
70}
71
72impl std::fmt::Display for Type {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 Type::Infer => write!(f, "infered type"),
76 Type::Any => write!(f, "opaque js value"),
77 Type::NumberLiteral(_) => write!(f, "numeric literal"),
78 Type::Int => write!(f, "int"),
79 Type::Float => write!(f, "float"),
80 Type::BigInt => write!(f, "bigint"),
81 Type::Bool => write!(f, "bool"),
82 Type::String => write!(f, "string"),
83 Type::NoReturn => write!(f, "noreturn"),
84 Type::Union(of) => {
85 for (i, t) in of.iter().enumerate() {
86 if i != 0 {
87 write!(f, " | ")?;
88 }
89 write!(f, "{}", t)?;
90 }
91 Ok(())
92 }
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
98pub struct Module {
99 pub value: Vec<Ast>,
52}100}
53101
54#[derive(Debug, Clone)]102#[derive(Debug, Clone)]
...@@ -65,15 +113,11 @@ pub struct Argument {...@@ -65,15 +113,11 @@ pub struct Argument {
65113
66#[derive(Debug, Clone)]114#[derive(Debug, Clone)]
67pub enum Ast {115pub enum Ast {
68 ConstantDeclaration {116 Declaration {
69 name: String,
70 return_type: Type,
71 value: Box<Ast>,
72 },
73 VariableDeclaration {
74 name: String,117 name: String,
75 return_type: Type,118 decl_type: Type,
76 value: Box<Ast>,119 value: Box<Ast>,
120 is_constant: bool,
77 },121 },
78 Operation {122 Operation {
79 operator: Operator,123 operator: Operator,
crates/checker/Cargo.toml created+14
...@@ -0,0 +1,14 @@
1[package]
2name = "checker"
3version = "0.1.0"
4edition = "2021"
5
6[lib]
7
8[dependencies]
9miette.workspace = true
10thiserror.workspace = true
11num.workspace = true
12
13ast = { path = "../ast" }
14util = { path = "../util" }
crates/checker/src/lib.rs created+203
...@@ -0,0 +1,203 @@
1use ast::{Ast, Module, Type};
2use miette::Result;
3
4use miette::Diagnostic;
5use thiserror::Error;
6
7#[derive(Error, Diagnostic, Debug)]
8#[error("TypeError")]
9pub struct TypeError {
10 #[help]
11 error: String,
12}
13
14impl TypeError {
15 pub fn new(error: String) -> Self {
16 Self { error }
17 }
18}
19
20pub fn check_module(block: &Module) -> Result<()> {
21 for statement in &block.value {
22 check_expression(statement)?;
23 }
24
25 Ok(())
26}
27
28pub fn resolve_type(expr: &Ast) -> Result<Type> {
29 match expr {
30 Ast::Number(value) => Ok(Type::NumberLiteral(value.clone())),
31 Ast::Declaration {
32 name,
33 decl_type,
34 value,
35 is_constant,
36 } => Ok(Type::NoReturn),
37 Ast::Operation {
38 operator,
39 left,
40 right,
41 } => merge_types(resolve_type(left)?, resolve_type(right)?),
42 Ast::Function {
43 name,
44 arguments,
45 return_type,
46 block,
47 } => todo!(),
48 Ast::FunctionCall { name, arguments } => todo!(),
49 Ast::If {
50 condition,
51 value,
52 else_value,
53 } => todo!(),
54 Ast::Return(_) => Ok(Type::NoReturn),
55 Ast::Public(_) => todo!(), // TODO: remove Public
56 Ast::Block(_) => todo!(),
57 Ast::Bool(_) => Ok(Type::Bool),
58 Ast::String(_) => Ok(Type::String),
59 Ast::Identifier(_) => todo!(),
60 Ast::BuiltIn(_) => todo!(),
61
62 Ast::Comment(_) => unreachable!(),
63 Ast::DocComment(_) => unreachable!(),
64 }
65}
66
67pub fn merge_types(a: Type, b: Type) -> Result<Type> {
68 match a {
69 Type::Infer => return Ok(b),
70 Type::Any => return Ok(Type::Any),
71 _ => {}
72 }
73
74 match b {
75 Type::Infer => return Ok(a),
76 Type::Any => return Ok(Type::Any),
77 _ => {}
78 }
79
80 if a == b {
81 return Ok(a);
82 }
83
84 Err(TypeError::new(format!("Cannot merge {} and {}", a, b)).into())
85}
86
87pub fn coerce_type(target: Type, source: Type) -> Result<Type> {
88 match source {
89 Type::Any => return Ok(target),
90 _ => {}
91 }
92 match target {
93 Type::Infer => Ok(source),
94 Type::Any => Ok(Type::Any),
95
96 Type::NumberLiteral(_) => unreachable!(),
97
98 Type::Int => match source {
99 Type::Int => Ok(Type::Int),
100 Type::Float => Ok(Type::Int),
101 Type::NumberLiteral(value) => {
102 if value.fract() != 0.0 {
103 Err(
104 TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
105 .into(),
106 )
107 } else if value > i32::MAX.into() || value < i32::MIN.into() {
108 Err(TypeError::new(format!(
109 "Numeric Literal '{}' is out of range for an integer ({}..{})",
110 value,
111 i32::MIN,
112 i32::MAX
113 ))
114 .into())
115 } else {
116 Ok(Type::Int)
117 }
118 }
119 t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
120 },
121
122 Type::BigInt => match source {
123 Type::Int => Ok(Type::Int),
124 Type::Float => Ok(Type::Int),
125 Type::NumberLiteral(value) => {
126 if value.fract() != 0.0 {
127 Err(
128 TypeError::new(format!("Numeric Literal '{}' is not an integer", value))
129 .into(),
130 )
131 } else {
132 Ok(Type::BigInt)
133 }
134 }
135 t => Err(TypeError::new(format!("Cannot coerce {} to int", t)).into()),
136 },
137
138 Type::Float => match source {
139 Type::Int => Ok(Type::Float),
140 Type::Float => Ok(Type::Float),
141 Type::NumberLiteral(value) => {
142 if value.fract() == 0.0 && value <= f64::MAX.into() && value >= f64::MIN.into() {
143 Ok(Type::Float)
144 } else {
145 Err(TypeError::new(format!("Numeric Literal")).into())
146 }
147 }
148 _ => Err(TypeError::new(format!("Cannot coerce int to float")).into()),
149 },
150
151 other => {
152 if other == source {
153 Ok(other)
154 } else {
155 Err(TypeError::new(format!("Cannot coerce {} to {}", source, other)).into())
156 }
157 }
158 }
159}
160
161pub fn check_expression(expr: &Ast) -> Result<()> {
162 match expr {
163 Ast::Declaration {
164 name,
165 decl_type: return_type,
166 value,
167 is_constant,
168 } => {
169 check_expression(value)?;
170 coerce_type(return_type.clone(), resolve_type(value)?)?;
171 }
172 _ => {}
173 // Ast::Operation {
174 // operator,
175 // left,
176 // right,
177 // } => Ok(()),
178 // Ast::Function {
179 // name,
180 // arguments,
181 // return_type,
182 // block,
183 // } => Ok(()),
184 // Ast::FunctionCall { name, arguments } => Ok(()),
185 // Ast::If {
186 // condition,
187 // value,
188 // else_value,
189 // } => Ok(()),
190 // Ast::Return(_) => Ok(()),
191 // Ast::Public(_) => Ok(()),
192 // Ast::Block(_) => Ok(()),
193 // Ast::Number(_) => Ok(()),
194 // Ast::Bool(_) => Ok(()),
195 // Ast::String(_) => Ok(()),
196 // Ast::Identifier(_) => Ok(()),
197 // Ast::BuiltIn(_) => Ok(()),
198 // Ast::Comment(_) => Ok(()),
199 // Ast::DocComment(_) => Ok(()),
200 }
201
202 Ok(())
203}
crates/cli/Cargo.toml+1
...@@ -16,3 +16,4 @@ tokenizer = { path = "../tokenizer" }...@@ -16,3 +16,4 @@ tokenizer = { path = "../tokenizer" }
16ast = { path = "../ast" }16ast = { path = "../ast" }
17js_ast = { path = "../js_ast" }17js_ast = { path = "../js_ast" }
18js_printer = { path = "../js_printer" }18js_printer = { path = "../js_printer" }
19checker = { path = "../checker" }
crates/cli/src/main.rs+38-5
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1use std::path::PathBuf;1use std::{io::Write, path::PathBuf};
22
3use checker;
3use clap::{Parser, Subcommand};4use clap::{Parser, Subcommand};
4use miette::Result;5use miette::Result;
56
...@@ -29,6 +30,9 @@ enum Commands {...@@ -29,6 +30,9 @@ enum Commands {
29 /// What javascript runtime to compile to30 /// What javascript runtime to compile to
30 #[arg(short, long, default_value = "deno")]31 #[arg(short, long, default_value = "deno")]
31 target: String,32 target: String,
33 /// Format the output javascript?
34 #[arg(long, default_value_t = true)]
35 format: bool,
32 },36 },
33 Run {37 Run {
34 file: PathBuf,38 file: PathBuf,
...@@ -51,11 +55,11 @@ pub fn transpile(content: String) -> Result<String> {...@@ -51,11 +55,11 @@ pub fn transpile(content: String) -> Result<String> {
51 // 2. Parse the tokens into an AST55 // 2. Parse the tokens into an AST
52 let blight_ast = ast::parse(tokens)?;56 let blight_ast = ast::parse(tokens)?;
53 // 3. Type check the AST57 // 3. Type check the AST
54 // type_check(tree.clone())?;58 checker::check_module(&blight_ast)?;
55 // 4. Optimize the AST59 // 4. Optimize the AST
56 // optimize(tree.clone())?;60 // optimize(tree.clone())?;
57 // 5. Translate the AST into javascript61 // 5. Translate the AST into javascript
58 let js_ast = js_ast::translate(blight_ast)?;62 let js_ast = js_ast::translate(&blight_ast)?;
59 // 6. Print the JS AST63 // 6. Print the JS AST
60 let javascript = js_printer::print(js_ast);64 let javascript = js_printer::print(js_ast);
61 // 7. Return the javascript65 // 7. Return the javascript
...@@ -70,9 +74,38 @@ fn main() -> Result<()> {...@@ -70,9 +74,38 @@ fn main() -> Result<()> {
70 file,74 file,
71 stdout,75 stdout,
72 target: _,76 target: _,
77 format,
73 } => {78 } => {
74 let contents = std::fs::read_to_string(file.clone()).unwrap();79 let contents = match std::fs::read_to_string(file.clone()) {
75 let result = transpile(contents.clone())?;80 Ok(contents) => contents,
81 Err(e) => {
82 eprintln!("Failed to read {}: {}", file.display(), e);
83 std::process::exit(1);
84 }
85 };
86
87 let mut result = transpile(contents.clone())?;
88
89 if format {
90 let mut formatter = std::process::Command::new("deno")
91 .stdin(std::process::Stdio::piped())
92 .stdout(std::process::Stdio::piped())
93 .stderr(std::process::Stdio::inherit())
94 .arg("fmt")
95 .arg("-")
96 .spawn()
97 .expect("Failed to execute 'deno fmt' on output");
98 let mut stdin = formatter.stdin.take().expect("Failed to open stdin");
99 stdin
100 .write_all(result.as_bytes())
101 .expect("Failed to write to stdin");
102 stdin.flush().expect("Failed to flush stdin");
103 drop(stdin);
104 let out = formatter
105 .wait_with_output()
106 .expect("Failed to execute 'deno fmt' on output");
107 result = String::from_utf8(out.stdout).unwrap();
108 }
76109
77 if stdout {110 if stdout {
78 println!("{}", result);111 println!("{}", result);
crates/js_ast/src/lib.rs+16-28
...@@ -2,7 +2,7 @@ use std::collections::HashMap;...@@ -2,7 +2,7 @@ use std::collections::HashMap;
22
3use miette::Result;3use miette::Result;
44
5use ast::Ast;5use ast::{Ast, Module};
66
7mod types;7mod types;
8pub use types::{JsExpression, JsStatement};8pub use types::{JsExpression, JsStatement};
...@@ -55,9 +55,10 @@ impl TranslateContext {...@@ -55,9 +55,10 @@ impl TranslateContext {
55 .last_mut()55 .last_mut()
56 .unwrap()56 .unwrap()
57 .statements57 .statements
58 .push(JsStatement::VariableDeclaration {58 .push(JsStatement::Declaration {
59 name: format!("${}", self.internal_counter),59 name: format!("${}", self.internal_counter),
60 value,60 value,
61 is_constant: false,
61 });62 });
62 self.internal_counter += 1;63 self.internal_counter += 1;
63 JsExpression::Identifier(format!("${}", self.internal_counter - 1))64 JsExpression::Identifier(format!("${}", self.internal_counter - 1))
...@@ -169,28 +170,18 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression...@@ -169,28 +170,18 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression
169170
170fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {171fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
171 match ast {172 match ast {
172 Ast::ConstantDeclaration {173 Ast::Declaration {
173 name,174 name,
174 return_type: _,175 decl_type: _,
175 value,
176 } => {
177 let handle = get_expression_handle(value, ctx);
178 let rename = ctx.declare_variable(name.clone());
179 ctx.push(JsStatement::ConstantDeclaration {
180 name: rename,
181 value: handle,
182 });
183 }
184 Ast::VariableDeclaration {
185 name,
186 return_type: _,
187 value,176 value,
177 is_constant,
188 } => {178 } => {
189 let handle = get_expression_handle(value, ctx);179 let handle = get_expression_handle(value, ctx);
190 let rename = ctx.declare_variable(name.clone());180 let rename = ctx.declare_variable(name.clone());
191 ctx.push(JsStatement::VariableDeclaration {181 ctx.push(JsStatement::Declaration {
192 name: rename,182 name: rename,
193 value: handle,183 value: handle,
184 is_constant: is_constant.clone(),
194 });185 });
195 }186 }
196 Ast::Function {187 Ast::Function {
...@@ -226,16 +217,13 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {...@@ -226,16 +217,13 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
226 translate_statement(value, ctx);217 translate_statement(value, ctx);
227 // I'm sorry, I just don't know how to get the original name properly... my bad218 // I'm sorry, I just don't know how to get the original name properly... my bad
228 match ctx.peek() {219 match ctx.peek() {
229 JsStatement::Function { name, .. }220 JsStatement::Function { name, .. } | JsStatement::Declaration { name, .. } => {
230 | JsStatement::VariableDeclaration { name, .. }
231 | JsStatement::ConstantDeclaration { name, .. } => {
232 let original_name = name.split("$").next().unwrap().to_string();221 let original_name = name.split("$").next().unwrap().to_string();
233 ctx.push(JsStatement::Export(Box::new(222 ctx.push(JsStatement::Export(Box::new(JsStatement::Declaration {
234 JsStatement::ConstantDeclaration {223 name: original_name,
235 name: original_name,224 value: JsExpression::Identifier(name.clone()),
236 value: JsExpression::Identifier(name.clone()),225 is_constant: false,
237 },226 })));
238 )));
239 }227 }
240 _ => {228 _ => {
241 panic!("Cannot make non-function public");229 panic!("Cannot make non-function public");
...@@ -337,10 +325,10 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {...@@ -337,10 +325,10 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) {
337 }325 }
338}326}
339327
340pub fn translate(_ast: Vec<Ast>) -> Result<Vec<JsStatement>> {328pub fn translate(module: &Module) -> Result<Vec<JsStatement>> {
341 let mut ctx = TranslateContext::new();329 let mut ctx = TranslateContext::new();
342330
343 for ast in _ast {331 for ast in module.value.iter() {
344 translate_statement(&ast, &mut ctx);332 translate_statement(&ast, &mut ctx);
345 }333 }
346334
crates/js_ast/src/types.rs+2-5
...@@ -33,13 +33,10 @@ pub enum JsStatement {...@@ -33,13 +33,10 @@ pub enum JsStatement {
33 Expression(JsExpression),33 Expression(JsExpression),
34 Export(Box<JsStatement>),34 Export(Box<JsStatement>),
35 Return(JsExpression),35 Return(JsExpression),
36 VariableDeclaration {36 Declaration {
37 name: String,
38 value: JsExpression,
39 },
40 ConstantDeclaration {
41 name: String,37 name: String,
42 value: JsExpression,38 value: JsExpression,
39 is_constant: bool,
43 },40 },
44 Function {41 Function {
45 name: String,42 name: String,
crates/js_printer/src/print_expression.rs+1-1
...@@ -230,7 +230,7 @@ mod tests {...@@ -230,7 +230,7 @@ mod tests {
230 assert_eq!(230 assert_eq!(
231 print_expression(&JsExpression::Function {231 print_expression(&JsExpression::Function {
232 arguments: vec!["a".to_string(), "b".to_string()],232 arguments: vec!["a".to_string(), "b".to_string()],
233 value: vec![JsStatement::VariableDeclaration {233 value: vec![JsStatement::Declaration {
234 name: "a".to_string(),234 name: "a".to_string(),
235 value: JsExpression::Number(1.0)235 value: JsExpression::Number(1.0)
236 }],236 }],
crates/js_printer/src/print_statement.rs+15-6
...@@ -7,11 +7,20 @@ pub fn print_statement(statement: &JsStatement) -> String {...@@ -7,11 +7,20 @@ pub fn print_statement(statement: &JsStatement) -> String {
7 JsStatement::Expression(value) => format!("{};", print_expression(value)),7 JsStatement::Expression(value) => format!("{};", print_expression(value)),
8 JsStatement::Export(value) => format!("export {}", print_statement(value)),8 JsStatement::Export(value) => format!("export {}", print_statement(value)),
9 JsStatement::Return(value) => format!("return {};", print_expression(value)),9 JsStatement::Return(value) => format!("return {};", print_expression(value)),
10 JsStatement::VariableDeclaration { name, value } => {10 JsStatement::Declaration {
11 format!("let {}={};", name, print_expression(value))11 name,
12 }12 value,
13 JsStatement::ConstantDeclaration { name, value } => {13 is_constant,
14 format!("const {}={};", name, print_expression(value))14 } => {
15 format!(
16 "{} {}={};",
17 match is_constant {
18 true => "const",
19 false => "let",
20 },
21 name,
22 print_expression(value)
23 )
15 }24 }
16 JsStatement::Function {25 JsStatement::Function {
17 name,26 name,
...@@ -66,7 +75,7 @@ mod tests {...@@ -66,7 +75,7 @@ mod tests {
66 #[test]75 #[test]
67 fn variable_declaration() {76 fn variable_declaration() {
68 assert_eq!(77 assert_eq!(
69 print_statement(&JsStatement::VariableDeclaration {78 print_statement(&JsStatement::Declaration {
70 name: "x".to_string(),79 name: "x".to_string(),
71 value: JsExpression::Number(1.0),80 value: JsExpression::Number(1.0),
72 }),81 }),
crates/tokenizer/src/lib.rs+36
...@@ -43,6 +43,42 @@ pub enum Token {...@@ -43,6 +43,42 @@ pub enum Token {
43 Operator(Operator),43 Operator(Operator),
44}44}
4545
46impl std::fmt::Display for Token {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 let token = match self {
49 Token::Identifier(_) => "an identifier".to_string(),
50 Token::Number(n) => n.to_string(),
51 Token::String(s) => format!("\"{}\"", s),
52 Token::BuiltIn(_) => "a builtin identifier".to_string(),
53 Token::Comment(_) => "a comment".to_string(),
54 Token::DocComment(_) => "a documentation comment".to_string(),
55 Token::True => "true".to_string(),
56 Token::False => "false".to_string(),
57 Token::Let => "let".to_string(),
58 Token::Var => "var".to_string(),
59 Token::If => "if".to_string(),
60 Token::Else => "else".to_string(),
61 Token::Return => "return".to_string(),
62 Token::Pub => "pub".to_string(),
63 Token::Function => "fn".to_string(),
64 Token::RightArrow => "->".to_string(),
65 Token::Comma => ",".to_string(),
66 Token::OpenParanthesis => "(".to_string(),
67 Token::ClosedParanthesis => ")".to_string(),
68 Token::OpenBracket => "{".to_string(),
69 Token::ClosedBracket => "}".to_string(),
70 Token::Colon => ":".to_string(),
71 Token::Equals => "=".to_string(),
72 Token::Semicolon => ";".to_string(),
73 Token::Operator(o) => o.clone().to_string(),
74 };
75
76 write!(f, "{}", token)
77 }
78}
79
80
81
46pub fn tokenize(content: String) -> Result<Vec<Token>> {82pub fn tokenize(content: String) -> Result<Vec<Token>> {
47 let mut tokens: Vec<Token> = Vec::new();83 let mut tokens: Vec<Token> = Vec::new();
48 let mut chars = content.chars().enumerate().peekable();84 let mut chars = content.chars().enumerate().peekable();
examples/basics/control.blight+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1let print: any = @raw("console.log");1let print = @raw("console.log");
22
3let x: bool = true;3let x: bool = true;
44
examples/basics/expressions.blight+1-1
...@@ -7,5 +7,5 @@ let x: int = 5 + {...@@ -7,5 +7,5 @@ let x: int = 5 + {
7 y7 y
8};8};
99
10let print: any = @raw("console.log");10let print = @raw("console.log");
11print(x); // 1011print(x); // 10
examples/basics/fib.blight+1-1
...@@ -8,5 +8,5 @@ pub fn fib(n: int) -> int {...@@ -8,5 +8,5 @@ pub fn fib(n: int) -> int {
8 }8 }
9}9}
1010
11let print: any = @raw("console.log");11let print = @raw("console.log");
12print(fib(10));12print(fib(10));
examples/basics/functions.blight+1-1
...@@ -4,5 +4,5 @@ fn bingus(x: int, y: int) -> int {...@@ -4,5 +4,5 @@ fn bingus(x: int, y: int) -> int {
44
5let result: int = bingus(10, 25);5let result: int = bingus(10, 25);
66
7let print: any = @raw("console.log");7let print = @raw("console.log");
8print(result);8print(result);
examples/basics/operations.blight+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1let a: int = 10 - 2 / 4 * 15;1let a: int = 10 - 2 / 4 * 15;
22
3let print: any = @raw("console.log");3let print = @raw("console.log");
4print(a);4print(a);
examples/basics/shadowing.blight+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1let print: any = @raw("console.log");1let print = @raw("console.log");
22
3let x: int = 0;3let x: int = 0;
4let x: int = 1;4let x: int = 1;
examples/basics/variables.blight+1-1
...@@ -6,5 +6,5 @@ let a: int = 0;...@@ -6,5 +6,5 @@ let a: int = 0;
6let b: float = 0;6let b: float = 0;
7let c: bigint = 0;7let c: bigint = 0;
88
9let print: any = @raw("console.log");9let print = @raw("console.log");
10print(x);10print(x);
examples/basics/weird.blight+1-1
...@@ -9,6 +9,6 @@ fn bingus(y: bool) -> int {...@@ -9,6 +9,6 @@ fn bingus(y: bool) -> int {
9 x9 x
10}10}
1111
12let print: any = @raw("console.log");12let print = @raw("console.log");
13print(bingus(true));13print(bingus(true));
14print(bingus(false));14print(bingus(false));
examples/interop/deno.blight+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1let writeTextFile: any = @raw("Deno.writeTextFile");1let writeTextFile = @raw("Deno.writeTextFile");
22
3fn x() -> string {3fn x() -> string {
4 "hello"4 "hello"