Reorganize using workspaces

This commit is contained in:
Andrew Morris
2023-02-27 12:35:37 +11:00
parent aafe1c1168
commit 44759d16a8
59 changed files with 515 additions and 412 deletions

36
Cargo.lock generated
View File

@@ -1446,6 +1446,16 @@ dependencies = [
"url",
]
[[package]]
name = "swc_demo"
version = "0.1.0"
dependencies = [
"swc",
"swc_common",
"swc_ecma_ast",
"swc_ecma_parser",
]
[[package]]
name = "swc_ecma_ast"
version = "0.76.0"
@@ -2147,10 +2157,9 @@ dependencies = [
]
[[package]]
name = "value_script"
name = "valuescript_compiler"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"queues",
"serde",
"serde_json",
@@ -2159,6 +2168,21 @@ dependencies = [
"swc_common",
"swc_ecma_ast",
"swc_ecma_parser",
]
[[package]]
name = "valuescript_vm"
version = "0.1.0"
[[package]]
name = "valuescript_wasm"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"serde",
"serde_json",
"valuescript_compiler",
"valuescript_vm",
"wasm-bindgen",
"wasm-bindgen-test",
"wee_alloc",
@@ -2176,6 +2200,14 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "vstc"
version = "0.1.0"
dependencies = [
"valuescript_compiler",
"valuescript_vm",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"

View File

@@ -1,41 +1,9 @@
[package]
name = "value_script"
version = "0.1.0"
edition = "2021"
[workspace]
[[bin]]
name = "vstc"
path = "src/vstc/main.rs"
test = false
bench = false
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
queues = "1.0.2"
serde = "1.0"
serde_json = "1.0"
swc_common = { version = "0.17.22", features=["tty-emitter"] }
swc_ecma_parser = "0.102.2"
swc = "0.168.3"
swc_ecma_ast = "0.76.0"
swc_atoms = "0.2"
# wasm-related
wasm-bindgen = "0.2.63"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
members = [
"valuescript_vm",
"valuescript_compiler",
"valuescript_wasm",
"vstc",
"swc_demo",
]

View File

@@ -1,21 +0,0 @@
mod vstc;
use wasm_bindgen::prelude::*;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
pub fn compile(source: &str) -> String {
let output = vstc::compile::compile(source);
return serde_json::to_string(&output).expect("Failed json serialization");
}
#[wasm_bindgen]
pub fn run(source: &str) -> String {
let run_result = vstc::run::run(source);
return serde_json::to_string(&run_result).expect("Failed json serialization");
}

View File

@@ -1,42 +0,0 @@
use std::{path::Path, sync::Arc};
use swc_ecma_ast::{EsVersion};
use swc_common::{
errors::{ColorConfig, Handler},
SourceMap, FileName,
};
use swc_ecma_parser::{TsConfig, Syntax};
fn main() {
let cm = Arc::<SourceMap>::default();
let handler = Handler::with_tty_emitter(
ColorConfig::Auto,
true,
false,
Some(cm.clone()),
);
let c = swc::Compiler::new(cm.clone());
// let fm = cm
// .load_file(Path::new("foo.js"))
// .expect("failed to load file");
let fm = cm.new_source_file(
FileName::Custom("test.js".into()),
"
function foo(x: number) {
if (x < 3) {
return 'lt3';
}
return 'nlt3';
}
".into(),
);
let result = c.parse_js(
fm,
&handler,
EsVersion::Es2020,
Syntax::Typescript(TsConfig::default()),
swc::config::IsModule::Bool(true),
None,
);
dbg!(result);
}

View File

@@ -1,11 +0,0 @@
mod assemble;
mod capture_finder;
pub mod compile;
pub mod diagnostic;
mod expression_compiler;
mod function_compiler;
mod name_allocator;
pub mod run;
mod scope;
pub mod scope_analysis;
pub mod virtual_machine;

12
swc_demo/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "swc_demo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_common = { version = "0.17.22", features=["tty-emitter"] }
swc_ecma_parser = "0.102.2"
swc = "0.168.3"
swc_ecma_ast = "0.76.0"

41
swc_demo/src/main.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::sync::Arc;
use swc_common::{
errors::{ColorConfig, Handler},
FileName, SourceMap,
};
use swc_ecma_ast::EsVersion;
use swc_ecma_parser::{Syntax, TsConfig};
fn main() {
let cm = Arc::<SourceMap>::default();
let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
let c = swc::Compiler::new(cm.clone());
// let fm = cm
// .load_file(Path::new("foo.js"))
// .expect("failed to load file");
let fm = cm.new_source_file(
FileName::Custom("test.js".into()),
"
function foo(x: number) {
if (x < 3) {
return 'lt3';
}
return 'nlt3';
}
"
.into(),
);
let result = c.parse_js(
fm,
&handler,
EsVersion::Es2020,
Syntax::Typescript(TsConfig::default()),
swc::config::IsModule::Bool(true),
None,
);
dbg!(&result);
drop(result);
}

View File

@@ -0,0 +1,16 @@
[package]
name = "valuescript_compiler"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = "1.0"
serde_json = "1.0"
swc_atoms = "0.2"
swc_common = { version = "0.17.22", features=["tty-emitter"] }
swc_ecma_parser = "0.102.2"
swc = "0.168.3"
swc_ecma_ast = "0.76.0"
queues = "1.0.2"

View File

@@ -1,48 +1,7 @@
use std::rc::Rc;
use std::process::exit;
use std::collections::HashMap;
use std::rc::Rc;
use std::str::FromStr;
pub fn command(args: &Vec<String>) {
if args.len() != 3 {
println!("ERROR: Unrecognized command\n");
show_help();
exit(1);
}
if args[2] == "-h" || args[2] == "--help" {
show_help();
return;
}
let read_result = std::fs::read_to_string(&args[2]);
if read_result.is_err() {
println!("Failed to read file {}", args[2]);
return;
}
let content = read_result.expect("");
let output_filename = "out.vsb";
let bytecode = assemble(&content);
let write_result = std::fs::write(output_filename, &*bytecode);
if write_result.is_err() {
println!("Failed to write file {}", output_filename);
std::process::exit(1);
}
}
fn show_help() {
println!("vstc assemble");
println!("");
println!("Convert ValueScript assembly to bytecode");
println!("");
println!("USAGE:");
println!(" vstc assemble <file>");
}
#[derive(Default)]
struct LocationMap {
references: HashMap<String, Vec<usize>>,
@@ -55,8 +14,9 @@ trait LocationMapper {
}
impl LocationMapper for LocationMap {
fn add_unresolved(&mut self, name: &String, output: &mut Vec<u8>){
self.references
fn add_unresolved(&mut self, name: &String, output: &mut Vec<u8>) {
self
.references
.entry(name.clone())
.or_default()
.push(output.len());
@@ -70,11 +30,7 @@ impl LocationMapper for LocationMap {
let location_optional = self.found_locations.get(name);
if location_optional.is_none() {
std::panic!(
"Unresolved reference to {} at {}",
name,
ref_locations[0],
);
std::panic!("Unresolved reference to {} at {}", name, ref_locations[0],);
}
let location = location_optional.unwrap();
@@ -146,9 +102,11 @@ impl<'a> Assembler<'a> {
fn parse_optional_whitespace(&mut self) {
loop {
match self.pos.peek() {
Some(' ') => {},
Some('\n') => {},
_ => { return; }
Some(' ') => {}
Some('\n') => {}
_ => {
return;
}
}
self.pos.next();
@@ -158,7 +116,10 @@ impl<'a> Assembler<'a> {
fn assemble_definition(&mut self) {
self.parse_exact("@");
let def_name = self.parse_identifier();
self.definitions_map.found_locations.insert(def_name, self.output.len());
self
.definitions_map
.found_locations
.insert(def_name, self.output.len());
self.parse_optional_whitespace();
self.parse_exact("=");
self.parse_optional_whitespace();
@@ -257,7 +218,9 @@ impl<'a> Assembler<'a> {
let mut res = "".to_string();
let leading_char = match pos.next() {
None => { return None; },
None => {
return None;
}
Some(c) => c,
};
@@ -269,7 +232,9 @@ impl<'a> Assembler<'a> {
loop {
match pos.next() {
None => { break; }
None => {
break;
}
Some(c) => {
if !is_identifier_char(c) {
break;
@@ -382,7 +347,9 @@ impl<'a> Assembler<'a> {
if next == ")" {
self.fn_data.register_count_pos = self.output.len();
self.output.push(0xff);
self.output.push((self.fn_data.register_map.len() - 3) as u8); // TODO: Handle >255
self
.output
.push((self.fn_data.register_map.len() - 3) as u8); // TODO: Handle >255
break;
}
@@ -393,7 +360,10 @@ impl<'a> Assembler<'a> {
let param_name = self.parse_identifier();
if self.fn_data.register_map.contains_key(param_name.as_str()) {
std::panic!("Unexpected duplicate parameter name at {}", self.get_pos_index());
std::panic!(
"Unexpected duplicate parameter name at {}",
self.get_pos_index()
);
}
self.get_register_index(param_name.as_str());
@@ -404,7 +374,9 @@ impl<'a> Assembler<'a> {
if next == ")" {
self.fn_data.register_count_pos = self.output.len();
self.output.push(0xff);
self.output.push((self.fn_data.register_map.len() - 3) as u8); // TODO: Handle >255
self
.output
.push((self.fn_data.register_map.len() - 3) as u8); // TODO: Handle >255
break;
}
}
@@ -415,7 +387,10 @@ impl<'a> Assembler<'a> {
loop {
self.parse_optional_whitespace();
let c = *self.pos.peek().expect("Expected instruction, label, or end of function");
let c = *self
.pos
.peek()
.expect("Expected instruction, label, or end of function");
if c == '}' {
self.output.push(Instruction::End as u8);
@@ -459,7 +434,7 @@ impl<'a> Assembler<'a> {
let instr = self.parse_instruction_word();
self.output.push(instr.clone() as u8);
for arg in get_instruction_layout(instr) {
match arg {
InstructionArg::Value => self.assemble_value(),
@@ -477,49 +452,44 @@ impl<'a> Assembler<'a> {
Some('%') => {
self.output.push(ValueType::Register as u8);
self.assemble_register();
},
}
Some('@') => {
self.parse_exact("@");
self.output.push(ValueType::Pointer as u8);
let definition_name = self.parse_identifier();
self.definitions_map.add_unresolved(&definition_name, &mut self.output);
},
self
.definitions_map
.add_unresolved(&definition_name, &mut self.output);
}
Some('$') => {
self.parse_exact("$");
self.output.push(ValueType::Builtin as u8);
self.assemble_builtin();
},
}
Some('[') => {
self.assemble_array();
},
Some('-' | '.' | '0' ..= '9') => {
}
Some('-' | '.' | '0'..='9') => {
self.assemble_number();
},
}
Some('"') => {
self.assemble_string();
},
}
Some('{') => {
self.assemble_object();
},
}
Some(ref_c) => {
let c = *ref_c;
let parsed = self.parse_one_of(&[
"void",
"undefined",
"null",
"false",
"true",
"",
]);
let parsed = self.parse_one_of(&["void", "undefined", "null", "false", "true", ""]);
match parsed.as_str() {
"void" => self.output.push(ValueType::Void as u8),
"undefined" => self.output.push(ValueType::Undefined as u8),
"null" => self.output.push(ValueType::Null as u8),
"false" => self.output.push(ValueType::False as u8),
"true" => self.output.push(ValueType::True as u8),
// TODO: Finish implementing the different values
_ => std::panic!(
"Unimplemented value type or unexpected character {} at {}",
@@ -527,7 +497,7 @@ impl<'a> Assembler<'a> {
self.get_pos_index(),
),
}
},
}
}
}
@@ -546,8 +516,8 @@ impl<'a> Assembler<'a> {
self.pos.next();
self.output.push(ValueType::End as u8);
break;
},
_ => {},
}
_ => {}
}
self.assemble_value();
@@ -610,17 +580,21 @@ impl<'a> Assembler<'a> {
advance_chars(&mut self.pos, label_name.len() + 1);
self.fn_data.labels_map.found_locations.insert(
label_name,
self.output.len(),
);
self
.fn_data
.labels_map
.found_locations
.insert(label_name, self.output.len());
}
fn assemble_label_read(&mut self) {
self.parse_optional_whitespace();
self.parse_exact(":");
let label_name = self.parse_identifier();
self.fn_data.labels_map.add_unresolved(&label_name, &mut self.output);
self
.fn_data
.labels_map
.add_unresolved(&label_name, &mut self.output);
}
fn assemble_number(&mut self) {
@@ -628,10 +602,12 @@ impl<'a> Assembler<'a> {
loop {
match self.pos.peek() {
Some('-' | '.' | 'e' | '0' ..= '9') => {
Some('-' | '.' | 'e' | '0'..='9') => {
num_string.push(self.pos.next().unwrap());
}
_ => { break; }
_ => {
break;
}
}
}
@@ -686,7 +662,9 @@ impl<'a> Assembler<'a> {
self.parse_exact("@");
self.output.push(ValueType::Pointer as u8);
let definition_name = self.parse_identifier();
self.definitions_map.add_unresolved(&definition_name, &mut self.output);
self
.definitions_map
.add_unresolved(&definition_name, &mut self.output);
} else if c == '}' {
self.output.push(ValueType::End as u8);
self.pos.next();
@@ -721,7 +699,10 @@ impl<'a> Assembler<'a> {
if get_result.is_none() {
// TODO: Support >255 registers
result = (self.fn_data.register_map.len() - 1) as u8;
self.fn_data.register_map.insert(register_name.to_string(), result);
self
.fn_data
.register_map
.insert(register_name.to_string(), result);
} else {
result = *get_result.unwrap();
}
@@ -766,8 +747,7 @@ pub fn assemble(content: &str) -> Rc<Vec<u8>> {
return Rc::new(assembler.output);
}
#[derive(Debug)]
#[derive(Clone)]
#[derive(Debug, Clone)]
enum Instruction {
End = 0x00,
Mov = 0x01,
@@ -874,20 +854,11 @@ fn get_instruction_layout(instruction: Instruction) -> Vec<InstructionArg> {
}
fn is_leading_identifier_char(c: char) -> bool {
return
c == '_' ||
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z')
;
return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
fn is_identifier_char(c: char) -> bool {
return
c == '_' ||
('0' <= c && c <= '9') ||
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z')
;
return c == '_' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
enum ValueType {

View File

@@ -1,7 +1,4 @@
use std::cell::RefCell;
use std::fs::File;
use std::io::prelude::*;
use std::process::exit;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
@@ -10,53 +7,13 @@ use swc_common::{errors::Handler, FileName, SourceMap, Spanned};
use swc_ecma_ast::EsVersion;
use swc_ecma_parser::{Syntax, TsConfig};
use super::diagnostic::{handle_diagnostics_cli, Diagnostic, DiagnosticLevel};
use super::diagnostic::{Diagnostic, DiagnosticLevel};
use super::expression_compiler::{string_literal, CompiledExpression, ExpressionCompiler};
use super::function_compiler::{FunctionCompiler, Functionish};
use super::name_allocator::NameAllocator;
use super::scope::{init_std_scope, MappedName, Scope, ScopeTrait};
use super::scope_analysis::ScopeAnalysis;
pub fn command(args: &Vec<String>) {
if args.len() != 3 {
println!("ERROR: Unrecognized command\n");
show_help();
exit(1);
}
let source = std::fs::read_to_string(&args[2]).expect("Failed to read file");
let (program_optional, parse_diagnostics) = parse(&source);
handle_diagnostics_cli(&args[2], &parse_diagnostics);
let program = match program_optional {
Some(program) => program,
None => exit(1),
};
let compiler_output = compile_program(&program);
handle_diagnostics_cli(&args[2], &compiler_output.diagnostics);
let mut file = File::create("out.vsm").expect("Couldn't create out.vsm");
for line in compiler_output.assembly {
file
.write_all(line.as_bytes())
.expect("Failed to write line");
file.write_all(b"\n").expect("Failed to write line");
}
}
fn show_help() {
println!("vstc compile");
println!("");
println!("Compile ValueScript");
println!("");
println!("USAGE:");
println!(" vstc compile <entry point>");
}
struct DiagnosticCollector {
diagnostics: Arc<Mutex<Vec<Diagnostic>>>,
}

View File

@@ -52,50 +52,3 @@ impl Diagnostic {
});
}
}
pub fn handle_diagnostics_cli(file_path: &String, diagnostics: &Vec<Diagnostic>) {
let mut has_error = false;
let text = std::fs::read_to_string(file_path).unwrap();
for diagnostic in diagnostics {
let (line, col) = pos_to_line_col(&text, diagnostic.span.lo.0);
println!(
"{}:{}:{}: {}: {}",
file_path, line, col, diagnostic.level, diagnostic.message
);
match diagnostic.level {
DiagnosticLevel::Error | DiagnosticLevel::InternalError => {
has_error = true;
}
DiagnosticLevel::Lint => {}
DiagnosticLevel::CompilerDebug => {}
}
}
if has_error {
std::process::exit(1);
}
}
fn pos_to_line_col(text: &String, pos: u32) -> (u32, u32) {
let mut line = 1u32;
let mut col = 1u32;
for (i, c) in text.chars().enumerate() {
if i as u32 == pos {
break;
}
if c == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
return (line, col);
}

View File

@@ -0,0 +1,14 @@
mod assemble;
mod capture_finder;
mod compile;
mod diagnostic;
mod expression_compiler;
mod function_compiler;
mod name_allocator;
mod scope;
mod scope_analysis;
pub use assemble::assemble;
pub use compile::compile;
pub use diagnostic::Diagnostic;
pub use diagnostic::DiagnosticLevel;

View File

@@ -11,9 +11,14 @@ pub enum NameId {
Builtin(Builtin),
}
// TODO: Make use of these in the next phase of the compiler, remove the
// allow(dead_code) attributes
#[derive(Clone)]
pub struct Capture {
#[allow(dead_code)]
ref_: swc_common::Span,
#[allow(dead_code)]
captor_id: OwnerId,
}

View File

@@ -0,0 +1,8 @@
[package]
name = "valuescript_vm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -1,22 +1,22 @@
mod vs_value;
mod vs_function;
mod vs_pointer;
mod operations;
mod bytecode_decoder;
mod virtual_machine;
mod instruction;
mod vs_object;
mod vs_array;
mod native_function;
mod builtins;
mod math;
mod vs_class;
mod bytecode_stack_frame;
mod stack_frame;
mod first_stack_frame;
mod array_higher_functions;
mod native_frame_function;
mod builtins;
mod bytecode_decoder;
mod bytecode_stack_frame;
mod debug;
mod first_stack_frame;
mod instruction;
mod math;
mod native_frame_function;
mod native_function;
mod operations;
mod stack_frame;
mod virtual_machine;
mod vs_array;
mod vs_class;
mod vs_function;
mod vs_object;
mod vs_pointer;
mod vs_value;
pub use virtual_machine::VirtualMachine;
pub use vs_value::ValTrait;

View File

@@ -1,9 +1,9 @@
use std::rc::Rc;
use super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::bytecode_decoder::BytecodeDecoder;
use super::stack_frame::{StackFrame, FrameStepResult};
use super::first_stack_frame::FirstStackFrame;
use super::stack_frame::{FrameStepResult, StackFrame};
use super::vs_value::{LoadFunctionResult, Val, ValTrait};
pub struct VirtualMachine {
pub frame: StackFrame,
@@ -50,14 +50,14 @@ impl VirtualMachine {
pub fn step(&mut self) {
match self.frame.step() {
FrameStepResult::Continue => {},
FrameStepResult::Continue => {}
FrameStepResult::Pop(call_result) => {
self.pop();
self.frame.apply_call_result(call_result);
},
}
FrameStepResult::Push(new_frame) => {
self.push(new_frame);
},
}
};
}

View File

@@ -1,12 +1,12 @@
use std::rc::Rc;
use std::str::FromStr;
use super::vs_function::VsFunction;
use super::vs_object::VsObject;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
use super::operations::{op_sub, op_submov};
use super::stack_frame::StackFrame;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
use super::vs_function::VsFunction;
use super::vs_object::VsObject;
#[derive(Clone)]
pub enum Val {
@@ -111,14 +111,16 @@ impl ValTrait for Val {
res += ",";
match val.typeof_() {
VsType::Undefined => {},
_ => { res += &val.val_to_string(); },
VsType::Undefined => {}
_ => {
res += &val.val_to_string();
}
};
}
res
}
},
}
Object(_) => "[object Object]".to_string(),
Function(_) => "[function]".to_string(),
Class(_) => "[class]".to_string(),
@@ -188,7 +190,7 @@ impl ValTrait for Val {
Class(_) => false,
Static(val) => val.is_primitive(), // TODO: false?
Custom(val) => val.is_primitive(),
}
};
}
fn to_primitive(&self) -> Val {
@@ -333,7 +335,7 @@ impl ValTrait for Val {
res
}
},
}
Val::Object(object) => {
if object.string_map.len() == 0 {
return "{}".into();
@@ -357,7 +359,7 @@ impl ValTrait for Val {
res += "}";
res
},
}
Val::Function(_) => "() => { [unavailable] }".to_string(),
Val::Class(_) => "class { [unavailable] }".to_string(),
Val::Static(val) => val.codify(),
@@ -395,15 +397,17 @@ impl std::fmt::Display for Val {
}
write!(f, " ]")
},
}
Val::Object(object) => {
if object.string_map.len() == 0 {
return f.write_str("{}");
}
match f.write_str("{ ") {
Ok(_) => {},
Err(e) => { return Err(e); },
Ok(_) => {}
Err(e) => {
return Err(e);
}
};
let mut first = true;
@@ -419,7 +423,7 @@ impl std::fmt::Display for Val {
}
f.write_str(" }")
},
}
Val::Function(_) => write!(f, "\x1b[36m[Function]\x1b[39m"),
Val::Class(_) => write!(f, "\x1b[36m[Class]\x1b[39m"),
@@ -432,7 +436,7 @@ impl std::fmt::Display for Val {
fn number_to_index(x: f64) -> Option<usize> {
if x < 0_f64 || x != x.floor() {
return None
return None;
}
return Some(x as usize);
@@ -451,8 +455,12 @@ fn stringify_string(str: &String) -> String {
};
match escape_seq {
Some(seq) => { res += seq; },
None => { res.push(c); }
Some(seq) => {
res += seq;
}
None => {
res.push(c);
}
};
}

View File

@@ -0,0 +1,33 @@
[package]
name = "valuescript_wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = "1.0"
serde_json = "1.0"
valuescript_compiler = { path = "../valuescript_compiler" }
valuescript_vm = { path = "../valuescript_vm" }
# wasm-related
wasm-bindgen = "0.2.63"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.13"

View File

@@ -0,0 +1,59 @@
use wasm_bindgen::prelude::*;
use valuescript_compiler::DiagnosticLevel;
use valuescript_vm::ValTrait;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[derive(serde::Serialize)]
struct RunResult {
diagnostics: Vec<valuescript_compiler::Diagnostic>,
output: Result<String, String>,
}
fn run_to_result(source: &str) -> RunResult {
let compiler_output = valuescript_compiler::compile(source);
let mut have_compiler_errors = false;
for diagnostic in &compiler_output.diagnostics {
match diagnostic.level {
DiagnosticLevel::Error => have_compiler_errors = true,
DiagnosticLevel::InternalError => have_compiler_errors = true,
_ => (),
}
}
if have_compiler_errors {
return RunResult {
diagnostics: compiler_output.diagnostics,
output: Err("Compile failed".into()),
};
}
let bytecode = valuescript_compiler::assemble(compiler_output.assembly.join("\n").as_str());
let mut vm = valuescript_vm::VirtualMachine::new();
let result = vm.run(&bytecode, &[]);
return RunResult {
diagnostics: compiler_output.diagnostics,
output: Ok(result.codify()),
};
}
#[wasm_bindgen]
pub fn compile(source: &str) -> String {
let output = valuescript_compiler::compile(source);
return serde_json::to_string(&output).expect("Failed json serialization");
}
#[wasm_bindgen]
pub fn run(source: &str) -> String {
let result = run_to_result(source);
return serde_json::to_string(&result).expect("Failed json serialization");
}

10
vstc/Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "vstc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
valuescript_compiler = { path = "../valuescript_compiler" }
valuescript_vm = { path = "../valuescript_vm" }

View File

@@ -0,0 +1,43 @@
use std::process::exit;
use valuescript_compiler::assemble;
pub fn assemble_command(args: &Vec<String>) {
if args.len() != 3 {
println!("ERROR: Unrecognized command\n");
show_help();
exit(1);
}
if args[2] == "-h" || args[2] == "--help" {
show_help();
return;
}
let read_result = std::fs::read_to_string(&args[2]);
if read_result.is_err() {
println!("Failed to read file {}", args[2]);
return;
}
let content = read_result.expect("");
let output_filename = "out.vsb";
let bytecode = assemble(&content);
let write_result = std::fs::write(output_filename, &*bytecode);
if write_result.is_err() {
println!("Failed to write file {}", output_filename);
std::process::exit(1);
}
}
fn show_help() {
println!("vstc assemble");
println!("");
println!("Convert ValueScript assembly to bytecode");
println!("");
println!("USAGE:");
println!(" vstc assemble <file>");
}

View File

@@ -0,0 +1,37 @@
use std::fs::File;
use std::io::Write;
use std::process::exit;
use super::handle_diagnostics_cli::handle_diagnostics_cli;
use valuescript_compiler::compile;
pub fn compile_command(args: &Vec<String>) {
if args.len() != 3 {
println!("ERROR: Unrecognized command\n");
show_help();
exit(1);
}
let source = std::fs::read_to_string(&args[2]).expect("Failed to read file");
let compiler_output = compile(&source);
handle_diagnostics_cli(&args[2], &compiler_output.diagnostics);
let mut file = File::create("out.vsm").expect("Couldn't create out.vsm");
for line in compiler_output.assembly {
file
.write_all(line.as_bytes())
.expect("Failed to write line");
file.write_all(b"\n").expect("Failed to write line");
}
}
fn show_help() {
println!("vstc compile");
println!("");
println!("Compile ValueScript");
println!("");
println!("USAGE:");
println!(" vstc compile <entry point>");
}

View File

@@ -0,0 +1,48 @@
use valuescript_compiler::{Diagnostic, DiagnosticLevel};
pub fn handle_diagnostics_cli(file_path: &String, diagnostics: &Vec<Diagnostic>) {
let mut has_error = false;
let text = std::fs::read_to_string(file_path).unwrap();
for diagnostic in diagnostics {
let (line, col) = pos_to_line_col(&text, diagnostic.span.lo.0);
println!(
"{}:{}:{}: {}: {}",
file_path, line, col, diagnostic.level, diagnostic.message
);
match diagnostic.level {
DiagnosticLevel::Error | DiagnosticLevel::InternalError => {
has_error = true;
}
DiagnosticLevel::Lint => {}
DiagnosticLevel::CompilerDebug => {}
}
}
if has_error {
std::process::exit(1);
}
}
fn pos_to_line_col(text: &String, pos: u32) -> (u32, u32) {
let mut line = 1u32;
let mut col = 1u32;
for (i, c) in text.chars().enumerate() {
if i as u32 == pos {
break;
}
if c == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
return (line, col);
}

View File

@@ -1,18 +1,15 @@
mod assemble;
mod capture_finder;
mod compile;
mod diagnostic;
mod expression_compiler;
mod function_compiler;
mod name_allocator;
mod run;
mod scope;
mod scope_analysis;
mod virtual_machine;
mod assemble_command;
mod compile_command;
mod handle_diagnostics_cli;
mod run_command;
use std::env;
use std::process::exit;
use assemble_command::assemble_command;
use compile_command::compile_command;
use run_command::run_command;
fn main() {
let args: Vec<String> = env::args().collect();
@@ -26,17 +23,17 @@ fn main() {
}
if args.len() >= 2 && args[1] == "assemble" {
assemble::command(&args);
assemble_command(&args);
return;
}
if args.len() >= 2 && args[1] == "run" {
run::command(&args);
run_command(&args);
return;
}
if args.len() >= 2 && args[1] == "compile" {
compile::command(&args);
compile_command(&args);
return;
}

View File

@@ -1,17 +1,12 @@
use std::ffi::OsStr;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;
use std::{ffi::OsStr, path::Path, process::exit};
use super::assemble::assemble;
use super::compile::compile;
use super::diagnostic::handle_diagnostics_cli;
use super::diagnostic::Diagnostic;
use super::diagnostic::DiagnosticLevel;
use super::virtual_machine::ValTrait;
use super::virtual_machine::VirtualMachine;
use valuescript_compiler::{assemble, compile};
use valuescript_vm::VirtualMachine;
pub fn command(args: &Vec<String>) {
use super::handle_diagnostics_cli::handle_diagnostics_cli;
pub fn run_command(args: &Vec<String>) {
if args.len() < 3 {
println!("ERROR: Unrecognized command\n");
show_help();
@@ -45,43 +40,6 @@ pub fn command(args: &Vec<String>) {
println!("{}", result);
}
#[derive(serde::Serialize)]
pub struct RunResult {
pub diagnostics: Vec<Diagnostic>,
pub output: Result<String, String>,
}
pub fn run(source: &str) -> RunResult {
let compiler_output = compile(source);
let mut have_compiler_errors = false;
for diagnostic in &compiler_output.diagnostics {
match diagnostic.level {
DiagnosticLevel::Error => have_compiler_errors = true,
DiagnosticLevel::InternalError => have_compiler_errors = true,
_ => (),
}
}
if have_compiler_errors {
return RunResult {
diagnostics: compiler_output.diagnostics,
output: Err("Compile failed".into()),
};
}
let bytecode = assemble(compiler_output.assembly.join("\n").as_str());
let mut vm = VirtualMachine::new();
let result = vm.run(&bytecode, &[]);
return RunResult {
diagnostics: compiler_output.diagnostics,
output: Ok(result.codify()),
};
}
enum RunFormat {
TypeScript,
Assembly,

View File

@@ -2,6 +2,9 @@ export async function initVslib() {
// deno-lint-ignore no-explicit-any
const wasm: Record<string, any> = (await WebAssembly.instantiateStreaming(
fetch(`${location.origin}/value_script_bg.wasm`),
{
"./valuescript_wasm_bg.js": { __wbindgen_throw },
},
)).instance.exports;
let WASM_VECTOR_LEN = 0;
@@ -128,6 +131,10 @@ export async function initVslib() {
}
}
function __wbindgen_throw(arg0: number, arg1: number) {
throw new Error(getStringFromWasm0(arg0, arg1));
}
return {
compile,
run,