Add initial WASM compiler framework (compiler incomplete)
This commit is contained in:
parent
f477673ebb
commit
f7be1ed33f
219
Cargo.lock
generated
219
Cargo.lock
generated
|
@ -2,12 +2,107 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
@ -20,12 +115,30 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "logos"
|
||||
version = "0.14.0"
|
||||
|
@ -62,6 +175,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "muss2"
|
||||
version = "2.0.0-alpha0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"muss2-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "muss2-lang"
|
||||
|
@ -71,6 +188,14 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "muss2-wasm"
|
||||
version = "2.0.0-alpha0"
|
||||
dependencies = [
|
||||
"muss2-lang",
|
||||
"wasm-encoder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
|
@ -105,6 +230,12 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.55"
|
||||
|
@ -122,6 +253,94 @@ version = "1.0.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.212.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
|
|
|
@ -11,8 +11,11 @@ readme = "README.md"
|
|||
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = [ "derive" ] }
|
||||
muss2-wasm = { version = "2.0.0-alpha0", path = "./crates/wasm" }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/lang"
|
||||
"crates/lang",
|
||||
"crates/wasm"
|
||||
]
|
||||
|
|
|
@ -10,7 +10,7 @@ mod test {
|
|||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
const ALL_TOKENS_STR: &str = "u n + - * / && || => x> ~> = . n_u_ :: is_a_ :: VaR1AbLe ( ) { } : ; -12345 12345.6789 \"char[]\" /* long\ncomment */ // short comment \n \n <>";
|
||||
const ALL_TOKENS_STR: &str = "u n + - * / && || => x> ~> = . n_u_ :: is_a_ :: VaR1AbLe ( ) { } : ; -12345 12345.6789 \"char[]\" /* long\ncomment */ // short comment \n \n <> ! | & << >> yield use as";
|
||||
|
||||
#[test]
|
||||
fn parse_everything() {
|
||||
|
@ -46,6 +46,14 @@ mod test {
|
|||
Token::ShortComment((" short comment ".into(), TokenInfo { line: 1, column: 11..29, index: 115..133 })),
|
||||
Token::Newline(TokenInfo { line: 2, column: 1..2, index: 134..135 }),
|
||||
Token::Generate(TokenInfo { line: 3, column: 1..3, index: 136..138 }),
|
||||
Token::Not(TokenInfo { line: 3, column: 4..5, index: 139..140 }),
|
||||
Token::BitwiseOr(TokenInfo { line: 3, column: 6..7, index: 141..142 }),
|
||||
Token::BitwiseAnd(TokenInfo { line: 3, column: 8..9, index: 143..144 }),
|
||||
Token::BitwiseShiftLeft(TokenInfo { line: 3, column: 10..12, index: 145..147 }),
|
||||
Token::BitwiseShiftRight(TokenInfo { line: 3, column: 13..15, index: 148..150 }),
|
||||
Token::Yield(TokenInfo { line: 3, column: 16..21, index: 151..156 }),
|
||||
Token::Use(TokenInfo { line: 3, column: 23..26, index: 158..161 }),
|
||||
Token::As(TokenInfo { line: 3, column: 28..30, index: 163..165 }),
|
||||
];
|
||||
|
||||
let mut actual = Vec::new();
|
||||
|
|
|
@ -64,6 +64,20 @@ pub enum Token {
|
|||
And(TokenInfo),
|
||||
#[token("||", callback = all_cb)]
|
||||
Or(TokenInfo),
|
||||
#[token("!", callback = all_cb)]
|
||||
Not(TokenInfo),
|
||||
// Bitwise operations
|
||||
#[token("|", callback = all_cb)]
|
||||
BitwiseOr(TokenInfo),
|
||||
#[token("&", callback = all_cb)]
|
||||
BitwiseAnd(TokenInfo),
|
||||
#[token("<<", callback = all_cb)]
|
||||
BitwiseShiftLeft(TokenInfo),
|
||||
#[token(">>", callback = all_cb)]
|
||||
BitwiseShiftRight(TokenInfo),
|
||||
// State operations
|
||||
#[token("yield", priority = 99, callback = all_cb)]
|
||||
Yield(TokenInfo),
|
||||
|
||||
// Functional
|
||||
#[token("=>", callback = all_cb)]
|
||||
|
@ -77,7 +91,6 @@ pub enum Token {
|
|||
|
||||
// Declarations
|
||||
|
||||
|
||||
// Basics
|
||||
#[token("=", callback = all_cb)]
|
||||
Equal(TokenInfo),
|
||||
|
@ -114,6 +127,12 @@ pub enum Token {
|
|||
#[regex("\\/\\/[^\n]*\n", priority = 1, callback = oneline_comment_cb)]
|
||||
ShortComment((String, TokenInfo)),
|
||||
|
||||
// Imports
|
||||
#[token("use", callback = all_cb)]
|
||||
Use(TokenInfo),
|
||||
#[token("as", callback = all_cb)]
|
||||
As(TokenInfo),
|
||||
|
||||
/// Ignore
|
||||
#[regex(r"\n", newline_cb)]
|
||||
Newline(TokenInfo),
|
||||
|
@ -211,6 +230,12 @@ impl Token {
|
|||
Self::Divide(_) => write!(result, "/"),
|
||||
Self::And(_) => write!(result, "&&"),
|
||||
Self::Or(_) => write!(result, "||"),
|
||||
Self::Not(_) => write!(result, "!"),
|
||||
Self::BitwiseOr(_) => write!(result, "|"),
|
||||
Self::BitwiseAnd(_) => write!(result, "&"),
|
||||
Self::BitwiseShiftLeft(_) => write!(result, "<<"),
|
||||
Self::BitwiseShiftRight(_) => write!(result, ">>"),
|
||||
Self::Yield(_) => write!(result, "yield"),
|
||||
Self::Map(_) => write!(result, "=>"),
|
||||
Self::Filter(_) => write!(result, "x>"),
|
||||
Self::Sort(_) => write!(result, "~>"),
|
||||
|
@ -230,6 +255,8 @@ impl Token {
|
|||
Self::String((s, _)) => write!(result, "\"{}\"", s),
|
||||
Self::LongComment((c, _)) => write!(result, "/*{}*/", c),
|
||||
Self::ShortComment((c, _)) => write!(result, "//{}\n", c),
|
||||
Self::Use(_) => write!(result, "use"),
|
||||
Self::As(_) => write!(result, "as"),
|
||||
Self::Newline(_) => write!(result, "\n"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,8 @@ pub struct Module {
|
|||
/// SetOp -> n
|
||||
/// SetOp -> u
|
||||
/// UnaryOp -> -
|
||||
/// UnaryOp -> !
|
||||
/// UnaryOp -> yield
|
||||
/// Literal -> "Name"
|
||||
/// Literal -> Integer
|
||||
/// Literal -> Float
|
||||
|
|
|
@ -6,7 +6,7 @@ mod parser;
|
|||
pub(crate) use parser::TokenParser;
|
||||
|
||||
mod tokens;
|
||||
pub use tokens::{SyntaxToken, Token, Literal, Op, Functional, Field, Path, Comment};
|
||||
pub use tokens::{SyntaxToken, Token, Literal, Op, Functional, Field, Path, Comment, Import};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
@ -14,7 +14,7 @@ mod test {
|
|||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
const ALL_TOKENS_STR: &str = "u n + - * / && || => x> ~> <> = n_u_::is_a_::VaR1AbLe .th1s.is_A_.f13Ld ( ) { } ; -404 -1234.5 \"\" /* block comment */ // line comment \n";
|
||||
const ALL_TOKENS_STR: &str = "u n + - * / && || ! & | >> << yield => x> ~> <> = n_u_::is_a_::VaR1AbLe .th1s.is_A_.f13Ld ( ) { } ; -404 -1234.5 \"\" /* block comment */ // line comment \n use as";
|
||||
|
||||
#[test]
|
||||
fn parse_everything() {
|
||||
|
@ -27,6 +27,12 @@ mod test {
|
|||
Token::Operation(Op::Divide),
|
||||
Token::Operation(Op::And),
|
||||
Token::Operation(Op::Or),
|
||||
Token::Operation(Op::Not),
|
||||
Token::Operation(Op::BitwiseAnd),
|
||||
Token::Operation(Op::BitwiseOr),
|
||||
Token::Operation(Op::ShiftRight),
|
||||
Token::Operation(Op::ShiftLeft),
|
||||
Token::Operation(Op::Yield),
|
||||
Token::Functional(Functional::Map),
|
||||
Token::Functional(Functional::Filter),
|
||||
Token::Functional(Functional::Sort),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub(crate) struct TokenParser<'a, I: core::iter::Iterator<Item=Result<crate::lexer::Token, crate::lexer::LexError>> + 'a> {
|
||||
pub struct TokenParser<'a, I: core::iter::Iterator<Item=Result<crate::lexer::Token, crate::lexer::LexError>> + 'a> {
|
||||
_idc: core::marker::PhantomData<&'a ()>,
|
||||
iter: I,
|
||||
lookahead: Option<crate::lexer::Token>,
|
||||
|
@ -64,6 +64,12 @@ impl <'a, I: core::iter::Iterator<Item=Result<crate::lexer::Token, crate::lexer:
|
|||
crate::lexer::Token::Divide(info) => super::Token::Operation(super::Op::Divide).with(info),
|
||||
crate::lexer::Token::And(info) => super::Token::Operation(super::Op::And).with(info),
|
||||
crate::lexer::Token::Or(info) => super::Token::Operation(super::Op::Or).with(info),
|
||||
crate::lexer::Token::Not(info) => super::Token::Operation(super::Op::Not).with(info),
|
||||
crate::lexer::Token::BitwiseAnd(info) => super::Token::Operation(super::Op::BitwiseAnd).with(info),
|
||||
crate::lexer::Token::BitwiseOr(info) => super::Token::Operation(super::Op::BitwiseOr).with(info),
|
||||
crate::lexer::Token::BitwiseShiftLeft(info) => super::Token::Operation(super::Op::ShiftLeft).with(info),
|
||||
crate::lexer::Token::BitwiseShiftRight(info) => super::Token::Operation(super::Op::ShiftRight).with(info),
|
||||
crate::lexer::Token::Yield(info) => super::Token::Operation(super::Op::Yield).with(info),
|
||||
crate::lexer::Token::Map(info) => super::Token::Functional(super::Functional::Map).with(info),
|
||||
crate::lexer::Token::Filter(info) => super::Token::Functional(super::Functional::Filter).with(info),
|
||||
crate::lexer::Token::Sort(info) => super::Token::Functional(super::Functional::Sort).with(info),
|
||||
|
@ -174,6 +180,8 @@ impl <'a, I: core::iter::Iterator<Item=Result<crate::lexer::Token, crate::lexer:
|
|||
crate::lexer::Token::String((s, info)) => super::Token::Literal(super::Literal::String(s)).with(info),
|
||||
crate::lexer::Token::LongComment((c, info)) => super::Token::Comment(super::tokens::Comment::Block(c)).with(info),
|
||||
crate::lexer::Token::ShortComment((c, info)) => super::Token::Comment(super::tokens::Comment::Line(c)).with(info),
|
||||
crate::lexer::Token::Use(info) => super::Token::Import(super::tokens::Import::Use).with(info),
|
||||
crate::lexer::Token::As(info) => super::Token::Import(super::tokens::Import::As).with(info),
|
||||
crate::lexer::Token::Newline(_) => panic!("Got non-ignored newline"),
|
||||
};
|
||||
Some(Ok(translated))
|
||||
|
|
|
@ -22,6 +22,7 @@ pub enum Token {
|
|||
|
||||
Literal(Literal),
|
||||
Comment(Comment),
|
||||
Import(Import),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
|
@ -30,12 +31,24 @@ impl Token {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct Field(pub Vec<String>);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
impl Field {
|
||||
pub fn as_str(&self) -> String {
|
||||
format!(".{}", self.0.join("."))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct Path(pub Vec<String>);
|
||||
|
||||
impl Path {
|
||||
pub fn as_str(&self) -> String {
|
||||
self.0.join("::")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Op {
|
||||
// Set operations
|
||||
|
@ -49,6 +62,14 @@ pub enum Op {
|
|||
// Logical operations
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
// Bitwise operations
|
||||
BitwiseAnd,
|
||||
BitwiseOr,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
// State operations
|
||||
Yield,
|
||||
}
|
||||
|
||||
impl Op {
|
||||
|
@ -62,6 +83,12 @@ impl Op {
|
|||
Self::Divide => "/",
|
||||
Self::And => "&&",
|
||||
Self::Or => "||",
|
||||
Self::Not => "!",
|
||||
Self::BitwiseAnd => "&",
|
||||
Self::BitwiseOr => "|",
|
||||
Self::ShiftLeft => "<<",
|
||||
Self::ShiftRight => ">>",
|
||||
Self::Yield => "yield",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +144,21 @@ impl Comment {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Import {
|
||||
Use,
|
||||
As,
|
||||
}
|
||||
|
||||
impl Import {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Use => "use",
|
||||
Self::As => "as",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn write_str(&self, result: &mut String) -> std::fmt::Result {
|
||||
use core::fmt::Write;
|
||||
|
@ -147,6 +189,7 @@ impl Token {
|
|||
Self::Semicolon => write!(result, ";"),
|
||||
Self::Literal(l) => write!(result, "{}", l.as_str()),
|
||||
Self::Comment(c) => write!(result, "{}", c.as_str()),
|
||||
Self::Import(i) => write!(result, "{}", i.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
crates/wasm/Cargo.toml
Normal file
9
crates/wasm/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "muss2-wasm"
|
||||
version = "2.0.0-alpha0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
wasm-encoder = { version = "0.212" }
|
||||
|
||||
muss2-lang = { version = "2.0.0-alpha0", path = "../lang" }
|
89
crates/wasm/src/codegen/function.rs
Normal file
89
crates/wasm/src/codegen/function.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use crate::symbols::{Symbols, FunctionType, Types, Locals};
|
||||
|
||||
pub fn generate_function(func: &FunctionType, symbols: &Symbols, types: &Types) -> Result<(wasm_encoder::FuncType, wasm_encoder::Function), Vec<super::CodegenError>> {
|
||||
match func.decl().type_ {
|
||||
muss2_lang::syntax::Functional::Map => generate_map_function(func, symbols, types),
|
||||
muss2_lang::syntax::Functional::Filter => todo!(),
|
||||
muss2_lang::syntax::Functional::Sort => todo!(),
|
||||
muss2_lang::syntax::Functional::Generate => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_map_function(func: &FunctionType, symbols: &Symbols, types: &Types) -> Result<(wasm_encoder::FuncType, wasm_encoder::Function), Vec<super::CodegenError>> {
|
||||
let mut errors = Vec::new();
|
||||
// transform parameters to wasm representation
|
||||
let mut params_ty = Vec::new();
|
||||
for (i, param) in func.decl().params.iter().enumerate() {
|
||||
if let Some(param_ty) = ¶m.type_ {
|
||||
if let Some(struct_index) = symbols.structs().nmap().get(¶m.name) {
|
||||
let param = wasm_encoder::ValType::Ref(wasm_encoder::RefType {
|
||||
nullable: false,
|
||||
heap_type: wasm_encoder::HeapType::Concrete(*struct_index as u32),
|
||||
});
|
||||
params_ty.push(param.clone());
|
||||
} else {
|
||||
errors.push(super::CodegenError::ParamTypeNotFound {
|
||||
param_i: i,
|
||||
name: param.name.clone(),
|
||||
type_: param_ty.clone(),
|
||||
func: func.decl().name.clone(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
errors.push(super::CodegenError::ParamTypeNotKnowable {
|
||||
param_i: i,
|
||||
name: param.name.clone(),
|
||||
func: func.decl().name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
return Err(errors)
|
||||
}
|
||||
// TODO transform ops to wasm representation
|
||||
let mut results_ty: Option<(muss2_lang::syntax::Path, wasm_encoder::ValType)> = None;
|
||||
let mut locals = Locals::new(func.decl().name.clone());
|
||||
let mut ops_meta = Vec::new();
|
||||
for (i, op) in func.decl().ops.iter().enumerate() {
|
||||
let result = super::ops::generate_op(op, symbols, &mut locals, types);
|
||||
match result {
|
||||
Ok(instr) => {
|
||||
if instr.is_return {
|
||||
if let Some(return_type_exp) = &results_ty {
|
||||
if !(&return_type_exp.0 == instr.type_.code_ty() && &return_type_exp.1 == instr.type_.wasm_ty()) {
|
||||
errors.push(super::CodegenError::ReturnTypeNotConsistent {
|
||||
return_i: i,
|
||||
func: func.decl().name.clone(),
|
||||
type_exp: return_type_exp.0.clone(),
|
||||
type_real: instr.type_.code_ty().to_owned(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
results_ty = Some((instr.type_.code_ty().to_owned(), instr.type_.wasm_ty().to_owned()));
|
||||
}
|
||||
}
|
||||
ops_meta.push(instr);
|
||||
},
|
||||
Err(mut e) => errors.append(&mut e),
|
||||
}
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
return Err(errors);
|
||||
}
|
||||
if let Some(results_ty) = results_ty {
|
||||
let functype = wasm_encoder::FuncType::new(params_ty, [results_ty.1]);
|
||||
let mut func = wasm_encoder::Function::new_with_locals_types(
|
||||
ops_meta.iter()
|
||||
.map(|m| m.type_.wasm_ty().to_owned())
|
||||
);
|
||||
for instrs in ops_meta.iter() {
|
||||
for instr in instrs.instr.iter() {
|
||||
func.instruction(&instr);
|
||||
}
|
||||
}
|
||||
Ok((functype, func))
|
||||
} else {
|
||||
errors.push(super::CodegenError::ReturnTypeNotKnowable { func: func.decl().name.clone() });
|
||||
Err(errors)
|
||||
}
|
||||
}
|
94
crates/wasm/src/codegen/mod.rs
Normal file
94
crates/wasm/src/codegen/mod.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
mod function;
|
||||
mod ops;
|
||||
mod structs;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum CodegenError {
|
||||
ParamTypeNotFound {
|
||||
param_i: usize,
|
||||
name: muss2_lang::syntax::Path,
|
||||
type_: muss2_lang::syntax::Path,
|
||||
func: muss2_lang::syntax::Path,
|
||||
},
|
||||
ParamTypeNotKnowable {
|
||||
param_i: usize,
|
||||
name: muss2_lang::syntax::Path,
|
||||
func: muss2_lang::syntax::Path,
|
||||
},
|
||||
ReturnTypeNotConsistent {
|
||||
return_i: usize,
|
||||
func: muss2_lang::syntax::Path,
|
||||
type_exp: muss2_lang::syntax::Path,
|
||||
type_real: muss2_lang::syntax::Path,
|
||||
},
|
||||
ReturnTypeNotKnowable {
|
||||
func: muss2_lang::syntax::Path,
|
||||
},
|
||||
AssignUndeclared {
|
||||
func: muss2_lang::syntax::Path,
|
||||
name: muss2_lang::syntax::Path,
|
||||
type_: muss2_lang::syntax::Path,
|
||||
},
|
||||
DeclareUntyped {
|
||||
func: muss2_lang::syntax::Path,
|
||||
name: muss2_lang::syntax::Path,
|
||||
},
|
||||
FieldTypeNotKnowable {
|
||||
param_i: usize,
|
||||
name: muss2_lang::syntax::Path,
|
||||
struc: muss2_lang::syntax::Path,
|
||||
},
|
||||
UnknownType {
|
||||
func: muss2_lang::syntax::Path,
|
||||
type_: muss2_lang::syntax::Path,
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CodegenError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ParamTypeNotFound { param_i, name, type_, func } => write!(f, "function `{}` param `{}`#{} type `{}` does not exist", func.as_str(), name.as_str(), param_i, type_.as_str()),
|
||||
Self::ParamTypeNotKnowable { param_i, name, func } => write!(f, "function `{}` param `{}`#{} type is unknown", func.as_str(), name.as_str(), param_i),
|
||||
Self::ReturnTypeNotConsistent { return_i, func, type_exp, type_real } => write!(f, "function `{}` returned `{}`#{} but expected `{}`", func.as_str(), type_real.as_str(), return_i, type_exp.as_str()),
|
||||
Self::ReturnTypeNotKnowable { func } => write!(f, "function `{}` return type is unknown", func.as_str()),
|
||||
Self::AssignUndeclared { func, name, type_ } => write!(f, "function `{}` assigns `{}` but `{}` was never declared", func.as_str(), type_.as_str(), name.as_str()),
|
||||
Self::DeclareUntyped { func, name } => write!(f, "function `{}` declares `{}` but type is unknown", func.as_str(), name.as_str()),
|
||||
Self::FieldTypeNotKnowable { param_i, name, struc } => write!(f, "struc `{}` field `{}`#{} type is unknown", struc.as_str(), name.as_str(), param_i),
|
||||
Self::UnknownType { func, type_ } => write!(f, "function `{}` uses unknown type `{}`", func.as_str(), type_.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(symbols: &crate::symbols::Symbols, module: &mut wasm_encoder::Module) -> Result<(), Vec<CodegenError>> {
|
||||
let mut errors = Vec::new();
|
||||
let mut types = crate::symbols::Types::new();
|
||||
let mut struct_table = wasm_encoder::TypeSection::new();
|
||||
for structure in symbols.structs().symbols() {
|
||||
let result = structs::generate_struct(structure, symbols, &types);
|
||||
match result {
|
||||
Ok((fields, tys)) => {
|
||||
struct_table.struct_(fields);
|
||||
tys.into_iter().for_each(|ty| types.push(ty));
|
||||
},
|
||||
Err(mut e) => {
|
||||
types.push(crate::symbols::TypeDecl::new_lazy_declared(structure.decl().name.clone(), types.symbols().len()));
|
||||
errors.append(&mut e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut fn_table = wasm_encoder::TypeSection::new();
|
||||
for function in symbols.functions().symbols() {
|
||||
let result = function::generate_function(function, symbols, &types);
|
||||
match result {
|
||||
Ok(func) => {
|
||||
fn_table.func_type(&func.0);
|
||||
},
|
||||
Err(mut e) => {
|
||||
fn_table.func_type(&wasm_encoder::FuncType::new([], []));
|
||||
errors.append(&mut e);
|
||||
}
|
||||
}
|
||||
}
|
||||
module.section(&fn_table);
|
||||
todo!()
|
||||
}
|
63
crates/wasm/src/codegen/ops.rs
Normal file
63
crates/wasm/src/codegen/ops.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use muss2_lang::statement::Op;
|
||||
use crate::symbols::{LocalDecl, Locals, Symbols, Types};
|
||||
|
||||
// Assumption: the 'result' of an instruction is the first/latest item on the stack
|
||||
|
||||
pub struct OpInstruction<'a> {
|
||||
pub instr: Vec<wasm_encoder::Instruction<'a>>,
|
||||
pub type_: LocalDecl,
|
||||
pub is_return: bool,
|
||||
pub is_assign: bool,
|
||||
}
|
||||
|
||||
pub fn generate_op<'a>(op: &Op, symbols: &Symbols, locals: &mut Locals, types: &Types) -> Result<OpInstruction<'a>, Vec<super::CodegenError>> {
|
||||
match op {
|
||||
Op::DeclareAssign(da) => {
|
||||
let mut op_instr = generate_op(&da.op, symbols, locals, types)?;
|
||||
locals.push(LocalDecl::new(op_instr.type_.wasm_ty().clone(), da.var.clone(), op_instr.type_.code_ty().clone()));
|
||||
op_instr.instr.push(wasm_encoder::Instruction::LocalSet(locals.by_name(&da.var).unwrap() as _));
|
||||
op_instr.is_assign = true;
|
||||
Ok(op_instr)
|
||||
},
|
||||
Op::Declare(d) => {
|
||||
if let Some(type_) = &d.type_ {
|
||||
let type_i = types.by_name(type_).ok_or_else(|| vec![
|
||||
super::CodegenError::UnknownType {
|
||||
func: locals.func().clone(),
|
||||
type_: type_.clone(),
|
||||
}
|
||||
])?;
|
||||
let type_decl = &types.symbols()[type_i];
|
||||
let decl = LocalDecl::new(type_decl.wasm_ty().to_owned(), d.var.clone(), type_.to_owned());
|
||||
locals.push(decl.clone());
|
||||
Ok(OpInstruction {
|
||||
instr: Vec::with_capacity(0),
|
||||
type_: decl,
|
||||
is_return: false,
|
||||
is_assign: false,
|
||||
})
|
||||
} else {
|
||||
Err(vec![
|
||||
super::CodegenError::DeclareUntyped {
|
||||
func: locals.func().clone(),
|
||||
name: d.var.clone(),
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
Op::Assign(a) => {
|
||||
let mut op_instr = generate_op(&a.op, symbols, locals, types)?;
|
||||
let local_index = locals.by_name(&a.var).ok_or_else(|| vec![
|
||||
super::CodegenError::AssignUndeclared {
|
||||
func: locals.func().clone(),
|
||||
name: a.var.clone(),
|
||||
type_: op_instr.type_.code_ty().clone(),
|
||||
}
|
||||
])?;
|
||||
op_instr.instr.push(wasm_encoder::Instruction::LocalSet(local_index as _));
|
||||
op_instr.is_assign = true;
|
||||
Ok(op_instr)
|
||||
},
|
||||
_ => todo!("finish compiler op generation")
|
||||
}
|
||||
}
|
50
crates/wasm/src/codegen/structs.rs
Normal file
50
crates/wasm/src/codegen/structs.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use crate::symbols::{Symbols, StructType, TypeDecl, Types, TypeVariant};
|
||||
|
||||
pub fn generate_struct(strct: &StructType, _symbols: &Symbols, types: &Types) -> Result<(Vec<wasm_encoder::FieldType>, Vec<TypeDecl>), Vec<super::CodegenError>> {
|
||||
let mut errors = Vec::new();
|
||||
let mut wasm_fields = Vec::new();
|
||||
let mut code_fields = std::collections::HashMap::new();
|
||||
let mut undeclared_tys = Vec::new();
|
||||
for (i, field) in strct.decl().params.iter().enumerate() {
|
||||
if let Some(field_ty) = &field.type_ {
|
||||
if let Some(field_ty_decl_i) = types.by_name(field_ty) {
|
||||
// field type is already declared, no need to re-declare it
|
||||
let field_decl = &types.symbols()[field_ty_decl_i];
|
||||
wasm_fields.push(wasm_encoder::FieldType {
|
||||
element_type: wasm_encoder::StorageType::Val(field_decl.wasm_ty().to_owned()),
|
||||
mutable: true,
|
||||
});
|
||||
code_fields.insert(field.name.clone(), field_decl.to_owned());
|
||||
} else {
|
||||
// field type is still undeclared, create a lazy version of it
|
||||
let lazy_i = types.symbols().len() + undeclared_tys.len() - 1;
|
||||
let lazy_ty = TypeDecl::new_lazy_declared(field_ty.clone(), lazy_i);
|
||||
wasm_fields.push(wasm_encoder::FieldType {
|
||||
element_type: wasm_encoder::StorageType::Val(lazy_ty.wasm_ty().to_owned()),
|
||||
mutable: true,
|
||||
});
|
||||
code_fields.insert(field.name.clone(), lazy_ty.clone());
|
||||
undeclared_tys.push(lazy_ty);
|
||||
}
|
||||
} else {
|
||||
errors.push(super::CodegenError::FieldTypeNotKnowable {
|
||||
param_i: i, name: field.name.clone(), struc: strct.decl().name.clone() })
|
||||
}
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
// this struct will be invalid since it has missing fields
|
||||
return Err(errors);
|
||||
}
|
||||
let mut all_new_tys = undeclared_tys; // rename because we're now using it for a fully declared type
|
||||
let val_ty = wasm_encoder::ValType::Ref(wasm_encoder::RefType {
|
||||
nullable: false,
|
||||
heap_type: wasm_encoder::HeapType::Concrete((types.symbols().len() + all_new_tys.len() - 1) as _),
|
||||
});
|
||||
let struct_ty = TypeDecl::new(strct.decl().name.clone(), TypeVariant::Structure {
|
||||
fields: code_fields,
|
||||
wasm_fields: wasm_fields.clone(),
|
||||
}, val_ty);
|
||||
// put all new types in one vector to return
|
||||
all_new_tys.push(struct_ty);
|
||||
Ok((wasm_fields, all_new_tys))
|
||||
}
|
16
crates/wasm/src/errors.rs
Normal file
16
crates/wasm/src/errors.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum CompileError {
|
||||
Language(muss2_lang::statement::LanguageError),
|
||||
Symbol(crate::symbols::SymbolError),
|
||||
Codegen(crate::codegen::CodegenError),
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CompileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Language(lang) => write!(f, "{}", lang),
|
||||
Self::Symbol(sym) => write!(f, "{}", sym),
|
||||
Self::Codegen(cg) => write!(f, "{}", cg),
|
||||
}
|
||||
}
|
||||
}
|
36
crates/wasm/src/lib.rs
Normal file
36
crates/wasm/src/lib.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
mod errors;
|
||||
pub use errors::CompileError;
|
||||
|
||||
mod codegen;
|
||||
mod symbols;
|
||||
|
||||
pub fn compile(s: &str) -> Result<wasm_encoder::Module, Vec<CompileError>> {
|
||||
let mut errors = Vec::new();
|
||||
let lang: Vec<muss2_lang::statement::Statement> = muss2_lang::statement::LanguageParser::lex(s).filter_map(|res| match res {
|
||||
Ok(statement) => Some(statement),
|
||||
Err(e) => {
|
||||
errors.push(CompileError::Language(e));
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
if !errors.is_empty() {
|
||||
return Err(errors);
|
||||
}
|
||||
let symbol_tbl = match symbols::Symbols::build_table(&mut lang.iter()) {
|
||||
Ok(symbols) => symbols,
|
||||
Err(e) => {
|
||||
e.into_iter().for_each(|e| errors.push(CompileError::Symbol(e)));
|
||||
return Err(errors);
|
||||
}
|
||||
};
|
||||
let mut module = wasm_encoder::Module::new();
|
||||
// compile
|
||||
if let Err(e) = codegen::generate(&symbol_tbl, &mut module) {
|
||||
e.into_iter().for_each(|e| errors.push(CompileError::Codegen(e)));
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(module)
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
52
crates/wasm/src/symbols/functions.rs
Normal file
52
crates/wasm/src/symbols/functions.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub struct FunctionType {
|
||||
decl: muss2_lang::statement::DeclareFun,
|
||||
module: muss2_lang::syntax::Path,
|
||||
}
|
||||
|
||||
impl FunctionType {
|
||||
pub fn with_decl(decl: muss2_lang::statement::DeclareFun, module: &super::modules::ModuleDecl) -> Self {
|
||||
Self {
|
||||
decl,
|
||||
module: module.as_root_path().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl(&self) -> &'_ muss2_lang::statement::DeclareFun {
|
||||
&self.decl
|
||||
}
|
||||
|
||||
pub fn module(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.module
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Functions {
|
||||
symbols: Vec<FunctionType>,
|
||||
name_map: HashMap<muss2_lang::syntax::Path, usize>,
|
||||
}
|
||||
|
||||
impl Functions {
|
||||
pub fn with_decls(iter: &mut impl core::iter::Iterator<Item=FunctionType>) -> Self {
|
||||
let symbols: Vec<_> = iter.collect();
|
||||
let name_map = symbols.iter().enumerate().map(|(i, item)| {
|
||||
let mut name = item.module.clone();
|
||||
name.0.append(&mut item.decl.name.0.clone());
|
||||
(name, i)
|
||||
}).collect();
|
||||
|
||||
Self {
|
||||
symbols,
|
||||
name_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> &'_ Vec<FunctionType> {
|
||||
&self.symbols
|
||||
}
|
||||
|
||||
pub fn nmap(&self) -> &'_ HashMap<muss2_lang::syntax::Path, usize> {
|
||||
&self.name_map
|
||||
}
|
||||
}
|
75
crates/wasm/src/symbols/locals.rs
Normal file
75
crates/wasm/src/symbols/locals.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LocalDecl {
|
||||
wasm_type: wasm_encoder::ValType,
|
||||
name: muss2_lang::syntax::Path,
|
||||
type_: muss2_lang::syntax::Path,
|
||||
}
|
||||
|
||||
impl LocalDecl {
|
||||
pub fn new(wasm_ty: wasm_encoder::ValType, name: muss2_lang::syntax::Path, code_ty: muss2_lang::syntax::Path) -> Self {
|
||||
Self {
|
||||
wasm_type: wasm_ty,
|
||||
name,
|
||||
type_: code_ty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_ty(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.type_
|
||||
}
|
||||
|
||||
pub fn code_name(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn wasm_ty(&self) -> &'_ wasm_encoder::ValType {
|
||||
&self.wasm_type
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Locals {
|
||||
symbols: Vec<LocalDecl>,
|
||||
name_map: HashMap<muss2_lang::syntax::Path, usize>,
|
||||
function: muss2_lang::syntax::Path,
|
||||
}
|
||||
|
||||
impl Locals {
|
||||
pub fn new(func: muss2_lang::syntax::Path) -> Self {
|
||||
Self {
|
||||
symbols: Vec::new(),
|
||||
name_map: HashMap::new(),
|
||||
function: func,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> &'_ Vec<LocalDecl> {
|
||||
&self.symbols
|
||||
}
|
||||
|
||||
pub fn nmap(&self) -> &'_ HashMap<muss2_lang::syntax::Path, usize> {
|
||||
&self.name_map
|
||||
}
|
||||
|
||||
pub fn func(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.function
|
||||
}
|
||||
|
||||
pub fn push(&mut self, decl: LocalDecl) {
|
||||
if let Some(&existing_entry) = self.name_map.get(&decl.name) {
|
||||
let existing_decl = &self.symbols[existing_entry];
|
||||
if existing_decl.wasm_type == decl.wasm_type {
|
||||
// an existing variable is getting re-declared with the same type and name
|
||||
// -- no need to create another local variable for it
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.name_map.insert(decl.name.clone(), self.symbols.len());
|
||||
self.symbols.push(decl);
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &muss2_lang::syntax::Path) -> Option<usize> {
|
||||
self.name_map.get(name).map(|x| *x)
|
||||
}
|
||||
}
|
88
crates/wasm/src/symbols/mod.rs
Normal file
88
crates/wasm/src/symbols/mod.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
mod functions;
|
||||
pub use functions::{FunctionType, Functions};
|
||||
mod modules;
|
||||
pub use modules::{ModuleDecl, Modules};
|
||||
mod structs;
|
||||
pub use structs::{StructType, Structs};
|
||||
|
||||
mod locals;
|
||||
pub use locals::{LocalDecl, Locals};
|
||||
mod types;
|
||||
pub use types::{TypeDecl, TypeVariant, Types};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum SymbolError {
|
||||
DuplicateType {
|
||||
name: muss2_lang::syntax::Path,
|
||||
module: muss2_lang::syntax::Path,
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for SymbolError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::DuplicateType { name, module } => write!(f, "duplicate type `{}` in module `{}`", name.as_str(), module.as_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Symbols {
|
||||
functions: functions::Functions,
|
||||
structs: structs::Structs,
|
||||
modules: modules::Modules,
|
||||
}
|
||||
|
||||
impl Symbols {
|
||||
pub fn build_table<'a>(iter: &mut impl core::iter::Iterator<Item=&'a muss2_lang::statement::Statement>) -> Result<Self, Vec<SymbolError>> {
|
||||
let module = modules::ModuleDecl::root();
|
||||
Self::build_submodule(iter, &module)
|
||||
}
|
||||
|
||||
fn build_submodule<'a>(iter: &mut impl core::iter::Iterator<Item=&'a muss2_lang::statement::Statement>, module: &modules::ModuleDecl) -> Result<Self, Vec<SymbolError>> {
|
||||
let mut function_decls = Vec::new();
|
||||
let mut struct_decls = Vec::new();
|
||||
let mut module_decls = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
for statement in iter {
|
||||
match statement {
|
||||
muss2_lang::statement::Statement::Declare(muss2_lang::statement::Declare::Function(fn_decl)) => {
|
||||
function_decls.push(functions::FunctionType::with_decl(fn_decl.to_owned(), module));
|
||||
},
|
||||
muss2_lang::statement::Statement::Declare(muss2_lang::statement::Declare::Type(ty_decl)) => {
|
||||
struct_decls.push(structs::StructType::with_decl(ty_decl.to_owned(), module));
|
||||
},
|
||||
muss2_lang::statement::Statement::Module(submodule) => {
|
||||
let submodule_decl = modules::ModuleDecl::from_decl(submodule.to_owned(), module);
|
||||
let subsymbols_res = Self::build_submodule(&mut submodule.inner.iter(), module);
|
||||
match subsymbols_res {
|
||||
Ok(subsymbols) => module_decls.push((submodule_decl, subsymbols)),
|
||||
Err(mut e) => errors.append(&mut e),
|
||||
}
|
||||
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Err(errors)
|
||||
} else {
|
||||
Ok(Self {
|
||||
functions: functions::Functions::with_decls(&mut function_decls.into_iter()),
|
||||
structs: structs::Structs::with_decls(&mut struct_decls.into_iter()),
|
||||
modules: modules::Modules::with_decls(&mut module_decls.into_iter()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn functions(&self) -> &'_ Functions {
|
||||
&self.functions
|
||||
}
|
||||
|
||||
pub fn modules(&self) -> &'_ Modules {
|
||||
&self.modules
|
||||
}
|
||||
|
||||
pub fn structs(&self) -> &'_ Structs {
|
||||
&self.structs
|
||||
}
|
||||
}
|
36
crates/wasm/src/symbols/modules.rs
Normal file
36
crates/wasm/src/symbols/modules.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub struct ModuleDecl(muss2_lang::syntax::Path);
|
||||
|
||||
impl ModuleDecl {
|
||||
pub fn from_decl(decl: muss2_lang::statement::Module, module: &Self) -> Self {
|
||||
let mut path = module.0.clone();
|
||||
path.0.append(&mut decl.name.0.clone());
|
||||
Self(path)
|
||||
}
|
||||
|
||||
pub fn root() -> Self {
|
||||
Self(muss2_lang::syntax::Path(Vec::new()))
|
||||
}
|
||||
|
||||
pub fn as_root_path(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Modules {
|
||||
symbols: Vec<(ModuleDecl, super::Symbols)>,
|
||||
name_map: HashMap<muss2_lang::syntax::Path, usize>,
|
||||
}
|
||||
|
||||
impl Modules {
|
||||
pub fn with_decls(iter: &mut impl core::iter::Iterator<Item=(ModuleDecl, super::Symbols)>) -> Self {
|
||||
let symbols: Vec<_> = iter.collect();
|
||||
let name_map = symbols.iter().enumerate().map(|(i, item)| (item.0.as_root_path().to_owned(), i)).collect();
|
||||
|
||||
Self {
|
||||
symbols,
|
||||
name_map,
|
||||
}
|
||||
}
|
||||
}
|
52
crates/wasm/src/symbols/structs.rs
Normal file
52
crates/wasm/src/symbols/structs.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub struct StructType {
|
||||
decl: muss2_lang::statement::DeclareType,
|
||||
module: muss2_lang::syntax::Path,
|
||||
}
|
||||
|
||||
impl StructType {
|
||||
pub fn with_decl(decl: muss2_lang::statement::DeclareType, module: &super::modules::ModuleDecl) -> Self {
|
||||
Self {
|
||||
decl,
|
||||
module: module.as_root_path().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl(&self) -> &'_ muss2_lang::statement::DeclareType {
|
||||
&self.decl
|
||||
}
|
||||
|
||||
pub fn module(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.module
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Structs {
|
||||
symbols: Vec<StructType>,
|
||||
name_map: HashMap<muss2_lang::syntax::Path, usize>,
|
||||
}
|
||||
|
||||
impl Structs {
|
||||
pub fn with_decls(iter: &mut impl core::iter::Iterator<Item=StructType>) -> Self {
|
||||
let symbols: Vec<_> = iter.collect();
|
||||
let name_map = symbols.iter().enumerate().map(|(i, item)| {
|
||||
let mut name = item.module.clone();
|
||||
name.0.append(&mut item.decl.name.0.clone());
|
||||
(name, i)
|
||||
}).collect();
|
||||
|
||||
Self {
|
||||
symbols,
|
||||
name_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> &'_ Vec<StructType> {
|
||||
&self.symbols
|
||||
}
|
||||
|
||||
pub fn nmap(&self) -> &'_ HashMap<muss2_lang::syntax::Path, usize> {
|
||||
&self.name_map
|
||||
}
|
||||
}
|
115
crates/wasm/src/symbols/types.rs
Normal file
115
crates/wasm/src/symbols/types.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TypeVariant {
|
||||
Function {
|
||||
params: Vec<TypeDecl>,
|
||||
wasm_type: wasm_encoder::FuncType,
|
||||
wasm: wasm_encoder::Function,
|
||||
},
|
||||
Structure {
|
||||
fields: HashMap<muss2_lang::syntax::Path, TypeDecl>,
|
||||
wasm_fields: Vec<wasm_encoder::FieldType>,
|
||||
},
|
||||
Primitive, // assumption: primitives are defined before type building is started
|
||||
Undeclared,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeDecl {
|
||||
name: muss2_lang::syntax::Path,
|
||||
wasm: wasm_encoder::ValType,
|
||||
variant: TypeVariant,
|
||||
}
|
||||
|
||||
impl TypeDecl {
|
||||
pub fn new(code_ty: muss2_lang::syntax::Path, variant_ty: TypeVariant, wasm_ty: wasm_encoder::ValType) -> Self {
|
||||
Self {
|
||||
name: code_ty,
|
||||
wasm: wasm_ty,
|
||||
variant: variant_ty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_lazy_declared(code_ty: muss2_lang::syntax::Path, index: usize) -> Self {
|
||||
Self {
|
||||
name: code_ty,
|
||||
wasm: wasm_encoder::ValType::Ref(
|
||||
wasm_encoder::RefType {
|
||||
nullable: false,
|
||||
heap_type: wasm_encoder::HeapType::Concrete(index as _),
|
||||
}
|
||||
),
|
||||
variant: TypeVariant::Undeclared,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_ty(&self) -> &'_ muss2_lang::syntax::Path {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn wasm_ty(&self) -> &'_ wasm_encoder::ValType {
|
||||
&self.wasm
|
||||
}
|
||||
|
||||
pub fn variant(&self) -> &'_ TypeVariant {
|
||||
&self.variant
|
||||
}
|
||||
|
||||
pub fn is_struct(&self) -> bool {
|
||||
matches!(self.variant, TypeVariant::Structure { .. })
|
||||
}
|
||||
|
||||
pub fn is_func(&self) -> bool {
|
||||
matches!(self.variant, TypeVariant::Function { .. })
|
||||
}
|
||||
|
||||
pub fn is_primi(&self) -> bool {
|
||||
matches!(self.variant, TypeVariant::Primitive)
|
||||
}
|
||||
|
||||
pub fn is_fully_declared(&self) -> bool {
|
||||
!matches!(self.variant, TypeVariant::Undeclared)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Types {
|
||||
symbols: Vec<< |