Skip to content

make standalone compiler dll #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ members = [
"somersault-vm",
"somersault-wasm",
"somersault-sbl",
"somersault-dll",
]
5 changes: 5 additions & 0 deletions dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# core + wasm
# sh s & cargo watch -c -q -w somersault-wasm -w somersault-core -- sh w

# core + web (angular)
sh ng & cargo watch -c -q -w somersault-core -- sh w
3 changes: 3 additions & 0 deletions ng
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cd somersault-web
npm i
npm start
2 changes: 1 addition & 1 deletion somersault-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ssc"
version = "0.1.0"
version = "0.2.254"
edition = "2021"

[dependencies]
Expand Down
17 changes: 9 additions & 8 deletions somersault-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ use anyhow::{bail, Result};
use somersault_core::{backends, toolchain};
use std::{env, path::Path};

fn compile(input: String) -> Result<Vec<u8>> {
let mut backend = backends::SaCsBackend::default();
fn compile(input: String, definitions: String) -> Result<Vec<u8>> {
let mut backend = backends::SaCsBackend::new(definitions);
match toolchain::run(input.as_bytes(), &mut backend) {
Ok(_) => Ok(backend.buf),
Err(e) => bail!("{e}"),
}
}

fn run(file_name: &str) -> Result<()> {
fn run(file_name: &str, definitions: String) -> Result<()> {
let path = Path::new(file_name);
let code = std::fs::read_to_string(path)?;
let buf = compile(code)?;
let buf = compile(code, definitions)?;
std::fs::write(path.with_extension("cs"), buf)?;
Ok(())
}

fn watch(file_name: &str) -> Result<()> {
fn watch(file_name: &str, definitions: String) -> Result<()> {
use std::time::Duration;

let path = Path::new(file_name);
Expand All @@ -29,7 +29,7 @@ fn watch(file_name: &str) -> Result<()> {
if metadata.modified()? != last_modified {
last_modified = metadata.modified()?;
let time = std::time::SystemTime::now();
match run(file_name) {
match run(file_name, definitions.clone()) {
Err(e) => {
println!("{e}");
}
Expand All @@ -42,17 +42,18 @@ fn watch(file_name: &str) -> Result<()> {
}
fn main() -> Result<()> {
let args: Vec<_> = env::args().collect();
let definitions = std::include_str!("../../data/sa.json");

// Usage: ssc <input> [--watch]
if args.len() > 1 {
let file_name = &args[1];

if let Err(e) = run(file_name) {
if let Err(e) = run(file_name, definitions.to_string()) {
println!("{e}");
}
if args.contains(&"--watch".to_string()) {
println!("Watching for changes... Press Ctrl+C to stop.");
watch(file_name)?;
watch(file_name, definitions.to_string())?;
}
} else {
println!("Usage: ssc <input>");
Expand Down
2 changes: 1 addition & 1 deletion somersault-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "somersault-core"
version = "0.2.150"
version = "0.2.254"
edition = "2021"

[dependencies]
Expand Down
1 change: 1 addition & 0 deletions somersault-core/src/backends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod sa_cs;

pub trait Backend {
fn process(&mut self, insts: Vec<compiler::Instruction>) -> Result<()>;
fn get_definitions(&self) -> String;
}

pub use sanny_text::SannyTextBackend;
Expand Down
13 changes: 12 additions & 1 deletion somersault-core/src/backends/sa_cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,24 @@ impl<'a> Into<Vec<u8>> for &OpcodeArgument {
}
}
}
#[derive(Default)]
pub struct SaCsBackend {
pub buf: Vec<u8>,
pub definitions: String
}

impl SaCsBackend {
pub fn new(definitions: String) -> Self {
Self {
buf: Vec::new(),
definitions,
}
}
}

impl Backend for SaCsBackend {
fn get_definitions(&self) -> String {
self.definitions.clone()
}
fn process(&mut self, insts: Vec<crate::compiler::Instruction>) -> Result<()> {
for inst in insts {
match inst {
Expand Down
5 changes: 4 additions & 1 deletion somersault-core/src/backends/sanny_text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::Backend;
use crate::compiler::Instruction;
use crate::argument::OpcodeArgument;
use crate::compiler::Instruction;
use crate::parser::ArgType;
use anyhow::{Ok, Result};

Expand Down Expand Up @@ -96,6 +96,9 @@ impl SannyTextBackend {
}

impl Backend for SannyTextBackend {
fn get_definitions(&self) -> String {
self.definitions.clone()
}
fn process(&mut self, insts: Vec<crate::compiler::Instruction>) -> Result<()> {
let library = somersault_sbl::parse_from(&self.definitions)?;

Expand Down
87 changes: 50 additions & 37 deletions somersault-core/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ pub struct ArrayDecl {
pub count: i32,
}

pub fn compile(program: AstNodeSpan) -> Result<Vec<Instruction>> {
let mut scopes = Scopes::new();
pub fn compile(program: AstNodeSpan, definitions: String) -> Result<Vec<Instruction>> {
let mut scopes = Scopes::new(definitions);
let mut instructions = vec![];

scopes.enter(ScopeType::Root, 0);
Expand All @@ -83,42 +83,42 @@ pub fn compile(program: AstNodeSpan) -> Result<Vec<Instruction>> {
}
}

// extra space to align buffer to 4 bytes
instructions.push(Instruction::RawBytes(vec![0, 0, 0, 0]));
instructions.push(Instruction::Label(scopes.frame_storage_label()));
let frame_storage = std::iter::repeat(0)
.take(scopes.get_frame_storage_size())
.collect();
let has_storage =
scopes.get_frame_storage_size() > 0 || scopes.get_persistent_storage_size() > 0;

instructions.push(Instruction::RawBytes(frame_storage));

// allocate persistent storage for static variables
if scopes.get_persistent_storage_size() > 0 {
prepend_vec(&mut instructions, heap_allocate_prelude(&mut scopes));
if has_storage || !scopes.get_exported_functions().is_empty() {
// extra space to align buffer to 4 bytes
// instructions.push(Instruction::RawBytes(vec![0, 0, 0, 0]));
// instructions.push(Instruction::Label(scopes.persistent_storage_label()));
let heap = std::iter::repeat(0)
.take(scopes.get_persistent_storage_size())
.collect();
instructions.push(Instruction::RawBytes(heap));
instructions.push(Instruction::RawBytes(vec![0, 0, 0, 0]));
instructions.push(Instruction::Label(scopes.frame_storage_label()));
}

// allocate temp storage for function's local variables
// must prepend vec before persistent for heap pointer calculation
prepend_vec(
&mut instructions,
temp_storage_allocate_prelude(&mut scopes),
);
if scopes.get_frame_storage_size() > 0 {
instructions.push(raw_buffer_inst(scopes.get_frame_storage_size()));
}

// allocate persistent storage for static variables
if scopes.get_persistent_storage_size() > 0 {
prepend_vec(
&mut instructions,
persistent_storage_allocate_prelude(&mut scopes),
);
instructions.push(raw_buffer_inst(scopes.get_persistent_storage_size()));
}
if has_storage {
// allocate temp storage for function's local variables
// must prepend vec before persistent for heap pointer calculation
prepend_vec(
&mut instructions,
frame_storage_allocate_prelude(&mut scopes),
);
}
// optional EXPT custom header
if !scopes.get_exported_functions().is_empty() {
let mut buf = vec![];

// magic
buf.extend(&[0xFF, 0x7F, 0xFE, 0x00, 0x00]);
// EXPT
buf.extend(&[0x45, 0x58, 0x50, 0x54]);
buf.extend(&[b'E', b'X', b'P', b'T']);
// export size
let size = scopes.calculate_export_section_size();
buf.extend(&size.to_le_bytes());
Expand Down Expand Up @@ -763,13 +763,13 @@ fn visit_export_function(
Instruction::Label(scopes.function_inner_label(&inner_node.name)),
);

let persistent_storage_prelude = heap_allocate_prelude(scopes);
let persistent_storage_prelude = persistent_storage_allocate_prelude(scopes);
for inst in persistent_storage_prelude.into_iter().rev() {
cache.insert(0, inst);
}

// must prepend vec before persistent for TIMERA elimination optimization
let temp_storage_prelude = temp_storage_allocate_prelude(scopes);
let temp_storage_prelude = frame_storage_allocate_prelude(scopes);
for inst in temp_storage_prelude.into_iter().rev() {
cache.insert(0, inst);
}
Expand Down Expand Up @@ -846,7 +846,9 @@ where
scope.reserve_space_for_pointers();
// remember how many local variables this function defines
// this will be used to create a new frame when another function is called (especially useful for recursive call, to not overwrite current state)
scope.set_frame_size(count_static_variables(&inner_node.body));
let frame_size = count_static_variables(&inner_node.body);
scope.set_frame_size(frame_size);
scopes.update_max_frame_size(frame_size);

if inner_node.is_exported {
let instructions = visit_export_function(inner_node, scopes)?;
Expand Down Expand Up @@ -1652,6 +1654,7 @@ fn get_assignment_opcode(lhs_ty: &ArgType, rhs_node: &AstNodeSpan) -> Result<u16
ArgType::String => match &rhs_node.node {
_ => Ok(OP_SET_LVAR_TEXT_LABEL16),
},
ArgType::IntOrFloat => bail!("Wrong type {} at line {}", lhs_ty, rhs_node.line),
}
}

Expand All @@ -1674,9 +1677,8 @@ fn get_assignment_opcode_binary(ty: &ArgType, op: &Token) -> Result<u16> {
// Token::Div => Ok(0x2708),
_ => bail!("Invalid operator {op}"),
},
ArgType::Void => bail!("Void type not allowed in binary operation"),
ArgType::String => {
bail!("String type not allowed in binary operation")
ArgType::Void | ArgType::String | ArgType::IntOrFloat => {
bail!("Type {} is not allowed in binary operation", ty)
}
}
}
Expand All @@ -1703,8 +1705,9 @@ fn get_logical_opcode(ty: &ArgType, op: &Token) -> Result<u16> {

_ => bail!("Invalid operator {op}"),
},
ArgType::Void => bail!("Void type not allowed in logical operation"),
ArgType::String => bail!("String type not allowed in logical operation"),
ArgType::Void | ArgType::String | ArgType::IntOrFloat => {
bail!("Type {} is not allowed in logical operation", ty)
}
}
}

Expand Down Expand Up @@ -1966,12 +1969,16 @@ fn match_types(ty1: ArgType, ty2: ArgType) -> bool {
(ty1, ty2) if ty1 == ty2 => true,
(ArgType::Int, ArgType::PInt32) => true,
(ArgType::PInt32, ArgType::Int) => true,
(ArgType::Int, ArgType::IntOrFloat) => true,
(ArgType::Float, ArgType::IntOrFloat) => true,
(ArgType::IntOrFloat, ArgType::Int) => true,
(ArgType::IntOrFloat, ArgType::Float) => true,
_ => false,
}
}

/// calculates the offset to static memory buffer and stores it in the special variable for the script to use
fn heap_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
fn persistent_storage_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
vec![
// optimization: persistent storage always follows frame storage which has fixed size
// heap_pointer = temp_pointer + frame_size / 4
Expand All @@ -1987,7 +1994,7 @@ fn heap_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
]
}

fn temp_storage_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
fn frame_storage_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
vec![
Instruction::OpcodeInst(OpcodeInst {
id: OP_GET_VAR_POINTER,
Expand Down Expand Up @@ -2020,3 +2027,9 @@ fn temp_storage_allocate_prelude(scopes: &mut Scopes) -> Vec<Instruction> {
}),
]
}

fn raw_buffer_inst(size: usize) -> Instruction {
let buf = std::iter::repeat(0).take(size).collect();

Instruction::RawBytes(buf)
}
Loading