Compare commits
53 Commits
visitor-wo
...
parser_com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc87f8cd90 | ||
|
|
a0955e07dc | ||
|
|
afcb10bb72 | ||
|
|
8de625e540 | ||
|
|
a2bd9a3985 | ||
|
|
e4a1a23f4d | ||
|
|
2cd325ba12 | ||
|
|
8218007f1c | ||
|
|
040ab11873 | ||
|
|
b967fa1911 | ||
|
|
4c718ed977 | ||
|
|
d20acf7166 | ||
|
|
efc8497235 | ||
|
|
d824b8d6ef | ||
|
|
4a1987b5a2 | ||
|
|
c96644ddce | ||
|
|
cc0ac83709 | ||
|
|
d6019e6f9a | ||
|
|
3344f6827d | ||
|
|
b38c4b3298 | ||
|
|
a2f30b6136 | ||
|
|
11a9a60a34 | ||
|
|
5bb1a245c4 | ||
|
|
1ffe61cf5f | ||
|
|
7495f30e16 | ||
|
|
82520aa28d | ||
|
|
129d9ec673 | ||
|
|
7825ef1eb9 | ||
|
|
f3ecdc61cb | ||
|
|
bf59e6cc63 | ||
|
|
c560c29b2d | ||
|
|
4dcd9d0198 | ||
|
|
7ac63160c5 | ||
|
|
8656992945 | ||
|
|
bb87a87848 | ||
|
|
2f467702e3 | ||
|
|
5ac5425fac | ||
|
|
944916d6af | ||
|
|
3906210db8 | ||
|
|
f7357d4498 | ||
|
|
1493d12a22 | ||
|
|
016d8fc900 | ||
|
|
86dc5eca02 | ||
|
|
e75958c2a2 | ||
|
|
7a56b6dfc0 | ||
|
|
f9633ebe55 | ||
|
|
854740a63f | ||
|
|
ca10481d7c | ||
|
|
26fa4a29ec | ||
|
|
97b59d7e70 | ||
|
|
92ad4767c8 | ||
|
|
7cabca2987 | ||
|
|
98e53a6d0f |
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -171,6 +171,11 @@ dependencies = [
|
|||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-type"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -346,6 +351,11 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nibble_vec"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
@@ -451,6 +461,10 @@ dependencies = [
|
|||||||
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "partis"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.7.24"
|
version = "0.7.24"
|
||||||
@@ -517,6 +531,15 @@ dependencies = [
|
|||||||
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_trie"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"endian-type 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nibble_vec 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -720,6 +743,7 @@ name = "schala"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"includedir_codegen 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"includedir_codegen 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"partis 0.1.0",
|
||||||
"schala-lang 0.1.0",
|
"schala-lang 0.1.0",
|
||||||
"schala-repl 0.1.0",
|
"schala-repl 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -735,6 +759,7 @@ dependencies = [
|
|||||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"radix_trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"schala-lang-codegen 0.1.0",
|
"schala-lang-codegen 0.1.0",
|
||||||
"schala-repl 0.1.0",
|
"schala-repl 0.1.0",
|
||||||
"stopwatch 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stopwatch 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1005,6 +1030,7 @@ dependencies = [
|
|||||||
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||||
"checksum ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f56c93cc076508c549d9bb747f79aa9b4eb098be7b8cad8830c3137ef52d1e00"
|
"checksum ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f56c93cc076508c549d9bb747f79aa9b4eb098be7b8cad8830c3137ef52d1e00"
|
||||||
|
"checksum endian-type 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||||
"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423"
|
"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423"
|
||||||
@@ -1027,6 +1053,7 @@ dependencies = [
|
|||||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||||
"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202"
|
"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202"
|
||||||
"checksum mortal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d96ca97d37cb87ea3d658e71c8c2e2f2d420eca5deec047c5a283aa810a900ff"
|
"checksum mortal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d96ca97d37cb87ea3d658e71c8c2e2f2d420eca5deec047c5a283aa810a900ff"
|
||||||
|
"checksum nibble_vec 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c8d77f3db4bce033f4d04db08079b2ef1c3d02b44e86f25d08886fafa7756ffa"
|
||||||
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
||||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||||
@@ -1046,6 +1073,7 @@ dependencies = [
|
|||||||
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
|
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
|
||||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
|
"checksum radix_trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "deafbff7bda0a58975ce92723aa18be10eae2e9271f3c3f48de52d131d9f248c"
|
||||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ authors = ["greg <greg.shuflin@protonmail.com>"]
|
|||||||
|
|
||||||
schala-repl = { path = "schala-repl" }
|
schala-repl = { path = "schala-repl" }
|
||||||
schala-lang = { path = "schala-lang/language" }
|
schala-lang = { path = "schala-lang/language" }
|
||||||
|
partis = { path="partis" }
|
||||||
# maaru-lang = { path = "maaru" }
|
# maaru-lang = { path = "maaru" }
|
||||||
# rukka-lang = { path = "rukka" }
|
# rukka-lang = { path = "rukka" }
|
||||||
# robo-lang = { path = "robo" }
|
# robo-lang = { path = "robo" }
|
||||||
|
|||||||
22
TODO.md
22
TODO.md
@@ -1,5 +1,24 @@
|
|||||||
|
# Plan of attack
|
||||||
|
|
||||||
|
1. modify visitor so it can handle scopes
|
||||||
|
-this is needed both to handle import scope correctly
|
||||||
|
-and also to support making FQSNs aware of function parameters
|
||||||
|
|
||||||
|
2. Once FQSNs are aware of function parameters, most of the Rc<String> things in eval.rs can go away
|
||||||
|
|
||||||
# TODO items
|
# TODO items
|
||||||
|
|
||||||
|
-use 'let' sigil in patterns for variables :
|
||||||
|
|
||||||
|
```
|
||||||
|
q is MyStruct(let a, Chrono::Trigga) then {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
-idea: what if there was something like React jsx syntas built in? i.e. a way to automatically transform some kind of markup
|
||||||
|
into a function call, cf. `<h1 prop="arg">` -> h1(prop=arg)
|
||||||
|
|
||||||
## General code cleanup
|
## General code cleanup
|
||||||
- I think I can restructure the parser to get rid of most instances of expect!, at least at the beginning of a rule
|
- I think I can restructure the parser to get rid of most instances of expect!, at least at the beginning of a rule
|
||||||
DONE -experiment with storing metadata via ItemIds on AST nodes (cf. https://rust-lang.github.io/rustc-guide/hir.html, https://github.com/rust-lang/rust/blob/master/src/librustc/hir/mod.rs )
|
DONE -experiment with storing metadata via ItemIds on AST nodes (cf. https://rust-lang.github.io/rustc-guide/hir.html, https://github.com/rust-lang/rust/blob/master/src/librustc/hir/mod.rs )
|
||||||
@@ -11,6 +30,9 @@ DONE -experiment with storing metadata via ItemIds on AST nodes (cf. https://rus
|
|||||||
-look at https://gitlab.haskell.org/ghc/ghc/wikis/pattern-synonyms
|
-look at https://gitlab.haskell.org/ghc/ghc/wikis/pattern-synonyms
|
||||||
2) the non-value-returning, default one like in rustc (cf. https://github.com/rust-unofficial/patterns/blob/master/patterns/visitor.md)
|
2) the non-value-returning, default one like in rustc (cf. https://github.com/rust-unofficial/patterns/blob/master/patterns/visitor.md)
|
||||||
|
|
||||||
|
-parser error - should report subset of AST parsed *so far*
|
||||||
|
- what if you used python 'def' syntax to define a function? what error message makes sense here?
|
||||||
|
|
||||||
## Reduction
|
## Reduction
|
||||||
- make a good type for actual language builtins to avoid string comparisons
|
- make a good type for actual language builtins to avoid string comparisons
|
||||||
|
|
||||||
|
|||||||
9
partis/Cargo.toml
Normal file
9
partis/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "partis"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["greg <greg.shuflin@protonmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
17
partis/src/lib.rs
Normal file
17
partis/src/lib.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
struct ParseError { }
|
||||||
|
|
||||||
|
enum ParseResult<'a, T> {
|
||||||
|
Success(T, &'a str),
|
||||||
|
Failure(ParseError),
|
||||||
|
Incomplete,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ impl Fold for RecursiveDescentFn {
|
|||||||
if self.parse_level != 0 {
|
if self.parse_level != 0 {
|
||||||
self.parse_level -= 1;
|
self.parse_level -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.map_err(|mut parse_error: ParseError| {
|
result.map_err(|mut parse_error: ParseError| {
|
||||||
parse_error.production_name = Some(stringify!(#ident).to_string());
|
parse_error.production_name = Some(stringify!(#ident).to_string());
|
||||||
parse_error
|
parse_error
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ ena = "0.11.0"
|
|||||||
stopwatch = "0.0.7"
|
stopwatch = "0.0.7"
|
||||||
derivative = "1.0.3"
|
derivative = "1.0.3"
|
||||||
colored = "1.8"
|
colored = "1.8"
|
||||||
|
radix_trie = "0.1.5"
|
||||||
|
|
||||||
schala-lang-codegen = { path = "../codegen" }
|
schala-lang-codegen = { path = "../codegen" }
|
||||||
schala-repl = { path = "../../schala-repl" }
|
schala-repl = { path = "../../schala-repl" }
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ pub enum StatementKind {
|
|||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
Declaration(Declaration),
|
Declaration(Declaration),
|
||||||
Import(ImportSpecifier),
|
Import(ImportSpecifier),
|
||||||
|
Module(ModuleSpecifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Block = Vec<Statement>;
|
pub type Block = Vec<Statement>;
|
||||||
@@ -297,3 +298,10 @@ pub enum ImportedNames {
|
|||||||
List(Vec<Rc<String>>)
|
List(Vec<Rc<String>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct ModuleSpecifier {
|
||||||
|
pub name: Rc<String>,
|
||||||
|
pub contents: Vec<Statement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,10 +68,6 @@ impl BinOp {
|
|||||||
let s = token_kind_to_sigil(op_tok)?;
|
let s = token_kind_to_sigil(op_tok)?;
|
||||||
Some(binop_precedences(s))
|
Some(binop_precedences(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_precedence(&self) -> i32 {
|
|
||||||
binop_precedences(&self.sigil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token_kind_to_sigil<'a>(tok: &'a TokenKind) -> Option<&'a str> {
|
fn token_kind_to_sigil<'a>(tok: &'a TokenKind) -> Option<&'a str> {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub trait ASTVisitor: Sized {
|
|||||||
fn invocation_argument(&mut self, _arg: &InvocationArgument) {}
|
fn invocation_argument(&mut self, _arg: &InvocationArgument) {}
|
||||||
fn formal_param(&mut self, _param: &FormalParam) {}
|
fn formal_param(&mut self, _param: &FormalParam) {}
|
||||||
fn import(&mut self, _import: &ImportSpecifier) {}
|
fn import(&mut self, _import: &ImportSpecifier) {}
|
||||||
|
fn module(&mut self, _module: &ModuleSpecifier) {}
|
||||||
fn qualified_name(&mut self, _name: &QualifiedName) {}
|
fn qualified_name(&mut self, _name: &QualifiedName) {}
|
||||||
fn nat_literal(&mut self, _n: u64) {}
|
fn nat_literal(&mut self, _n: u64) {}
|
||||||
fn float_literal(&mut self, _f: f64) {}
|
fn float_literal(&mut self, _f: f64) {}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ impl ASTVisitor for Tester {
|
|||||||
#[test]
|
#[test]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let mut tester = Tester { count: 0, float_count: 0 };
|
let mut tester = Tester { count: 0, float_count: 0 };
|
||||||
let ast = quick_ast(r#"
|
let (ast, _) = quick_ast(r#"
|
||||||
import gragh
|
import gragh
|
||||||
|
|
||||||
let a = 20 + 84
|
let a = 20 + 84
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ fn statement<V: ASTVisitor>(v: &mut V, statement: &Statement) {
|
|||||||
declaration(v, decl);
|
declaration(v, decl);
|
||||||
},
|
},
|
||||||
Import(ref import_spec) => v.import(import_spec),
|
Import(ref import_spec) => v.import(import_spec),
|
||||||
|
Module(ref module_spec) => {
|
||||||
|
v.module(module_spec);
|
||||||
|
walk_block(v, &module_spec.contents);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +52,7 @@ fn declaration<V: ASTVisitor>(v: &mut V, decl: &Declaration) {
|
|||||||
Binding { name, constant, type_anno, expr } => {
|
Binding { name, constant, type_anno, expr } => {
|
||||||
v.binding(name, *constant, type_anno.as_ref(), expr);
|
v.binding(name, *constant, type_anno.as_ref(), expr);
|
||||||
v.type_annotation(type_anno.as_ref());
|
v.type_annotation(type_anno.as_ref());
|
||||||
|
v.expression(&expr);
|
||||||
expression(v, &expr);
|
expression(v, &expr);
|
||||||
},
|
},
|
||||||
Impl { type_name, interface_name, block } => {
|
Impl { type_name, interface_name, block } => {
|
||||||
@@ -76,6 +81,7 @@ fn expression<V: ASTVisitor>(v: &mut V, expression: &Expression) {
|
|||||||
|
|
||||||
fn call<V: ASTVisitor>(v: &mut V, f: &Expression, args: &Vec<InvocationArgument>) {
|
fn call<V: ASTVisitor>(v: &mut V, f: &Expression, args: &Vec<InvocationArgument>) {
|
||||||
v.expression(f);
|
v.expression(f);
|
||||||
|
expression(v, f);
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
v.invocation_argument(arg);
|
v.invocation_argument(arg);
|
||||||
invocation_argument(v, arg);
|
invocation_argument(v, arg);
|
||||||
@@ -114,6 +120,7 @@ fn named_struct<V: ASTVisitor>(v: &mut V, n: &QualifiedName, fields: &Vec<(Rc<St
|
|||||||
fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option<&TypeIdentifier>, body: &Block) {
|
fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option<&TypeIdentifier>, body: &Block) {
|
||||||
for param in params {
|
for param in params {
|
||||||
v.formal_param(param);
|
v.formal_param(param);
|
||||||
|
formal_param(v, param);
|
||||||
}
|
}
|
||||||
v.type_annotation(type_anno);
|
v.type_annotation(type_anno);
|
||||||
v.block(body);
|
v.block(body);
|
||||||
@@ -121,7 +128,10 @@ fn lambda<V: ASTVisitor>(v: &mut V, params: &Vec<FormalParam>, type_anno: Option
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn formal_param<V: ASTVisitor>(v: &mut V, param: &FormalParam) {
|
fn formal_param<V: ASTVisitor>(v: &mut V, param: &FormalParam) {
|
||||||
param.default.as_ref().map(|p| v.expression(p));
|
param.default.as_ref().map(|p| {
|
||||||
|
v.expression(p);
|
||||||
|
expression(v, p);
|
||||||
|
});
|
||||||
v.type_annotation(param.anno.as_ref());
|
v.type_annotation(param.anno.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +230,12 @@ fn condition_arm<V: ASTVisitor>(v: &mut V, arm: &ConditionArm) {
|
|||||||
},
|
},
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
arm.guard.as_ref().map(|guard| {
|
||||||
|
v.expression(guard);
|
||||||
|
expression(v, guard);
|
||||||
|
});
|
||||||
|
v.block(&arm.body);
|
||||||
|
walk_block(v, &arm.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_pattern<V: ASTVisitor>(v: &mut V, pat: &Pattern) {
|
fn walk_pattern<V: ASTVisitor>(v: &mut V, pat: &Pattern) {
|
||||||
@@ -231,18 +247,23 @@ fn walk_pattern<V: ASTVisitor>(v: &mut V, pat: &Pattern) {
|
|||||||
walk_pattern(v, pat);
|
walk_pattern(v, pat);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TupleStruct(_, patterns) => {
|
TupleStruct(qualified_name, patterns) => {
|
||||||
|
v.qualified_name(qualified_name);
|
||||||
for pat in patterns {
|
for pat in patterns {
|
||||||
v.pattern(pat);
|
v.pattern(pat);
|
||||||
walk_pattern(v, pat);
|
walk_pattern(v, pat);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Record(_, name_and_patterns) => {
|
Record(qualified_name, name_and_patterns) => {
|
||||||
|
v.qualified_name(qualified_name);
|
||||||
for (_, pat) in name_and_patterns {
|
for (_, pat) in name_and_patterns {
|
||||||
v.pattern(pat);
|
v.pattern(pat);
|
||||||
walk_pattern(v, pat);
|
walk_pattern(v, pat);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
VarOrName(qualified_name) => {
|
||||||
|
v.qualified_name(qualified_name);
|
||||||
|
},
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::schala::SymbolTableHandle;
|
||||||
use crate::util::ScopeStack;
|
use crate::util::ScopeStack;
|
||||||
use crate::reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternative, Subpattern};
|
use crate::reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternative, Subpattern};
|
||||||
use crate::symbol_table::{SymbolSpec, Symbol, SymbolTable, ScopeSegment, ScopeSegmentKind, FullyQualifiedSymbolName};
|
use crate::symbol_table::{SymbolSpec, Symbol, SymbolTable, FullyQualifiedSymbolName};
|
||||||
use crate::builtin::Builtin;
|
use crate::builtin::Builtin;
|
||||||
|
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
pub struct State<'a> {
|
pub struct State<'a> {
|
||||||
values: ScopeStack<'a, Rc<String>, ValueEntry>,
|
values: ScopeStack<'a, Rc<String>, ValueEntry>,
|
||||||
symbol_table_handle: Rc<RefCell<SymbolTable>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
pub fn new(symbol_table_handle: Rc<RefCell<SymbolTable>>) -> State<'a> {
|
pub fn new() -> State<'a> {
|
||||||
let values = ScopeStack::new(Some(format!("global")));
|
let values = ScopeStack::new(Some(format!("global")));
|
||||||
State { values, symbol_table_handle }
|
State { values }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_print(&self) -> String {
|
pub fn debug_print(&self) -> String {
|
||||||
@@ -31,7 +29,6 @@ impl<'a> State<'a> {
|
|||||||
fn new_frame(&'a self, items: &'a Vec<Node>, bound_vars: &BoundVars) -> State<'a> {
|
fn new_frame(&'a self, items: &'a Vec<Node>, bound_vars: &BoundVars) -> State<'a> {
|
||||||
let mut inner_state = State {
|
let mut inner_state = State {
|
||||||
values: self.values.new_scope(None),
|
values: self.values.new_scope(None),
|
||||||
symbol_table_handle: self.symbol_table_handle.clone(),
|
|
||||||
};
|
};
|
||||||
for (bound_var, val) in bound_vars.iter().zip(items.iter()) {
|
for (bound_var, val) in bound_vars.iter().zip(items.iter()) {
|
||||||
if let Some(bv) = bound_var.as_ref() {
|
if let Some(bv) = bound_var.as_ref() {
|
||||||
@@ -70,12 +67,12 @@ fn paren_wrapped_vec(terms: impl Iterator<Item=String>) -> String {
|
|||||||
|
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
fn to_repl(&self, symbol_table: &SymbolTable) -> String {
|
fn to_repl(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Node::Expr(e) => e.to_repl(symbol_table),
|
Node::Expr(e) => e.to_repl(),
|
||||||
Node::PrimObject { name, items, .. } if items.len() == 0 => format!("{}", name),
|
Node::PrimObject { name, items, .. } if items.len() == 0 => format!("{}", name),
|
||||||
Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl(symbol_table)))),
|
Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
|
||||||
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl(symbol_table)))),
|
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn is_true(&self) -> bool {
|
fn is_true(&self) -> bool {
|
||||||
@@ -100,12 +97,10 @@ impl Expr {
|
|||||||
fn to_node(self) -> Node {
|
fn to_node(self) -> Node {
|
||||||
Node::Expr(self)
|
Node::Expr(self)
|
||||||
}
|
}
|
||||||
fn to_repl(&self, symbol_table: &SymbolTable) -> String {
|
fn to_repl(&self) -> String {
|
||||||
use self::Lit::*;
|
use self::Lit::*;
|
||||||
use self::Func::*;
|
use self::Func::*;
|
||||||
|
|
||||||
let _ = symbol_table;
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Expr::Lit(ref l) => match l {
|
Expr::Lit(ref l) => match l {
|
||||||
Nat(n) => format!("{}", n),
|
Nat(n) => format!("{}", n),
|
||||||
@@ -122,7 +117,7 @@ impl Expr {
|
|||||||
Expr::Constructor { type_name, arity, .. } => {
|
Expr::Constructor { type_name, arity, .. } => {
|
||||||
format!("<constructor for `{}` arity {}>", type_name, arity)
|
format!("<constructor for `{}` arity {}>", type_name, arity)
|
||||||
},
|
},
|
||||||
Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl(symbol_table))),
|
Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl())),
|
||||||
_ => format!("{:?}", self),
|
_ => format!("{:?}", self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,8 +152,7 @@ impl<'a> State<'a> {
|
|||||||
for statement in ast.0 {
|
for statement in ast.0 {
|
||||||
match self.statement(statement) {
|
match self.statement(statement) {
|
||||||
Ok(Some(ref output)) if repl => {
|
Ok(Some(ref output)) if repl => {
|
||||||
let ref symbol_table = self.symbol_table_handle.borrow();
|
acc.push(Ok(output.to_repl()))
|
||||||
acc.push(Ok(output.to_repl(symbol_table)))
|
|
||||||
},
|
},
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -212,7 +206,10 @@ impl<'a> State<'a> {
|
|||||||
Node::Expr(expr) => match expr {
|
Node::Expr(expr) => match expr {
|
||||||
literal @ Lit(_) => Ok(Node::Expr(literal)),
|
literal @ Lit(_) => Ok(Node::Expr(literal)),
|
||||||
Call { box f, args } => self.call_expression(f, args),
|
Call { box f, args } => self.call_expression(f, args),
|
||||||
Sym(v) => self.handle_sym(v),
|
Sym(name) => Ok(match self.values.lookup(&name) {
|
||||||
|
Some(ValueEntry::Binding { val, .. }) => val.clone(),
|
||||||
|
None => return Err(format!("Could not look up symbol {}", name))
|
||||||
|
}),
|
||||||
Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }),
|
Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }),
|
||||||
constructor @ Constructor { .. } => Ok(Node::Expr(constructor)),
|
constructor @ Constructor { .. } => Ok(Node::Expr(constructor)),
|
||||||
func @ Func(_) => Ok(Node::Expr(func)),
|
func @ Func(_) => Ok(Node::Expr(func)),
|
||||||
@@ -264,7 +261,6 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
let mut func_state = State {
|
let mut func_state = State {
|
||||||
values: self.values.new_scope(name.map(|n| format!("{}", n))),
|
values: self.values.new_scope(name.map(|n| format!("{}", n))),
|
||||||
symbol_table_handle: self.symbol_table_handle.clone(),
|
|
||||||
};
|
};
|
||||||
for (param, val) in params.into_iter().zip(args.into_iter()) {
|
for (param, val) in params.into_iter().zip(args.into_iter()) {
|
||||||
let val = func_state.expression(Node::Expr(val))?;
|
let val = func_state.expression(Node::Expr(val))?;
|
||||||
@@ -343,13 +339,11 @@ impl<'a> State<'a> {
|
|||||||
|
|
||||||
/* builtin functions */
|
/* builtin functions */
|
||||||
(IOPrint, &[ref anything]) => {
|
(IOPrint, &[ref anything]) => {
|
||||||
let ref symbol_table = self.symbol_table_handle.borrow();
|
print!("{}", anything.to_repl());
|
||||||
print!("{}", anything.to_repl(symbol_table));
|
|
||||||
Expr::Unit.to_node()
|
Expr::Unit.to_node()
|
||||||
},
|
},
|
||||||
(IOPrintLn, &[ref anything]) => {
|
(IOPrintLn, &[ref anything]) => {
|
||||||
let ref symbol_table = self.symbol_table_handle.borrow();
|
println!("{}", anything.to_repl());
|
||||||
println!("{}", anything.to_repl(symbol_table));
|
|
||||||
Expr::Unit.to_node()
|
Expr::Unit.to_node()
|
||||||
},
|
},
|
||||||
(IOGetLine, &[]) => {
|
(IOGetLine, &[]) => {
|
||||||
@@ -458,45 +452,4 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
Err(format!("{:?} failed pattern match", cond))
|
Err(format!("{:?} failed pattern match", cond))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO if I don't need to lookup by name here...
|
|
||||||
fn handle_sym(&mut self, name: Rc<String>) -> EvalResult<Node> {
|
|
||||||
use self::ValueEntry::*;
|
|
||||||
use self::Func::*;
|
|
||||||
//TODO add a layer of indirection here to talk to the symbol table first, and only then look up
|
|
||||||
//in the values table
|
|
||||||
|
|
||||||
let symbol_table = self.symbol_table_handle.borrow();
|
|
||||||
let value = symbol_table.lookup_by_fqsn(&fqsn!(name ; tr));
|
|
||||||
Ok(match value {
|
|
||||||
Some(Symbol { local_name, spec, .. }) => match spec {
|
|
||||||
//TODO I'll need this type_name later to do a table lookup
|
|
||||||
SymbolSpec::DataConstructor { type_name: _type_name, type_args, .. } => {
|
|
||||||
if type_args.len() == 0 {
|
|
||||||
Node::PrimObject { name: local_name.clone(), tag: 0, items: vec![] }
|
|
||||||
} else {
|
|
||||||
return Err(format!("This data constructor thing not done"))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SymbolSpec::Func(_) => match self.values.lookup(&name) {
|
|
||||||
Some(Binding { val: Node::Expr(Expr::Func(UserDefined { name, params, body })), .. }) => {
|
|
||||||
Node::Expr(Expr::Func(UserDefined { name: name.clone(), params: params.clone(), body: body.clone() }))
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
SymbolSpec::RecordConstructor { .. } => return Err(format!("This shouldn't be a record!")),
|
|
||||||
SymbolSpec::Binding => match self.values.lookup(&name) {
|
|
||||||
Some(Binding { val, .. }) => val.clone(),
|
|
||||||
None => return Err(format!("Symbol {} exists in symbol table but not in evaluator table", name))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//TODO ideally this should be returning a runtime error if this is ever None, but it's not
|
|
||||||
//handling all bindings correctly yet
|
|
||||||
//None => return Err(format!("Couldn't find value {}", name)),
|
|
||||||
None => match self.values.lookup(&name) {
|
|
||||||
Some(Binding { val, .. }) => val.clone(),
|
|
||||||
None => return Err(format!("Couldn't find value {}", name)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ use crate::reduced_ast::reduce;
|
|||||||
use crate::eval::State;
|
use crate::eval::State;
|
||||||
|
|
||||||
fn evaluate_all_outputs(input: &str) -> Vec<Result<String, String>> {
|
fn evaluate_all_outputs(input: &str) -> Vec<Result<String, String>> {
|
||||||
let symbol_table = Rc::new(RefCell::new(SymbolTable::new()));
|
let (mut ast, source_map) = crate::util::quick_ast(input);
|
||||||
let mut state = State::new(symbol_table);
|
let source_map = Rc::new(RefCell::new(source_map));
|
||||||
let mut ast = crate::util::quick_ast(input);
|
let symbol_table = Rc::new(RefCell::new(SymbolTable::new(source_map)));
|
||||||
state.symbol_table_handle.borrow_mut().add_top_level_symbols(&ast).unwrap();
|
symbol_table.borrow_mut().add_top_level_symbols(&ast).unwrap();
|
||||||
{
|
{
|
||||||
let mut t = &mut state.symbol_table_handle.borrow_mut();
|
let mut scope_resolver = ScopeResolver::new(symbol_table.clone());
|
||||||
let mut scope_resolver = ScopeResolver::new(&mut t);
|
|
||||||
let _ = scope_resolver.resolve(&mut ast);
|
let _ = scope_resolver.resolve(&mut ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reduced = reduce(&ast, &state.symbol_table_handle.borrow());
|
let reduced = reduce(&ast, &symbol_table.borrow());
|
||||||
|
let mut state = State::new();
|
||||||
let all_output = state.evaluate(reduced, true);
|
let all_output = state.evaluate(reduced, true);
|
||||||
all_output
|
all_output
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ fn scopes() {
|
|||||||
test_in_fresh_env!(scope_ok, "10");
|
test_in_fresh_env!(scope_ok, "10");
|
||||||
let scope_ok = r#"
|
let scope_ok = r#"
|
||||||
let a = 20
|
let a = 20
|
||||||
fn haha() {
|
fn queque() {
|
||||||
let a = 10
|
let a = 10
|
||||||
a
|
a
|
||||||
}
|
}
|
||||||
@@ -258,3 +258,12 @@ milta()(10)
|
|||||||
"#;
|
"#;
|
||||||
test_in_fresh_env!(source, "43");
|
test_in_fresh_env!(source, "43");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_all() {
|
||||||
|
let source = r#"
|
||||||
|
type Option<T> = Some(T) | None
|
||||||
|
import Option::*
|
||||||
|
let x = Some(9); if x is Some(q) then { q } else { 0 }"#;
|
||||||
|
test_in_fresh_env!(source, "9");
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#![feature(trace_macros)]
|
#![feature(trace_macros)]
|
||||||
#![feature(custom_attribute)]
|
|
||||||
//#![feature(unrestricted_attribute_tokens)]
|
//#![feature(unrestricted_attribute_tokens)]
|
||||||
#![feature(slice_patterns, box_patterns, box_syntax)]
|
#![feature(slice_patterns, box_patterns, box_syntax)]
|
||||||
|
|
||||||
@@ -18,6 +17,7 @@ extern crate schala_lang_codegen;
|
|||||||
extern crate ena;
|
extern crate ena;
|
||||||
extern crate derivative;
|
extern crate derivative;
|
||||||
extern crate colored;
|
extern crate colored;
|
||||||
|
extern crate radix_trie;
|
||||||
|
|
||||||
|
|
||||||
macro_rules! bx {
|
macro_rules! bx {
|
||||||
@@ -39,6 +39,7 @@ mod scope_resolution;
|
|||||||
mod builtin;
|
mod builtin;
|
||||||
mod reduced_ast;
|
mod reduced_ast;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
mod source_map;
|
||||||
|
|
||||||
mod schala;
|
mod schala;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//! ```text
|
//! ```text
|
||||||
//! program := (statement delimiter)* EOF
|
//! program := (statement delimiter)* EOF
|
||||||
//! delimiter := NEWLINE | ";"
|
//! delimiter := NEWLINE | ";"
|
||||||
//! statement := expression | declaration | import
|
//! statement := expression | declaration | import | module
|
||||||
//! block := "{" (statement delimiter)* "}"
|
//! block := "{" (statement delimiter)* "}"
|
||||||
//! declaration := type_declaration | func_declaration | binding_declaration | impl_declaration
|
//! declaration := type_declaration | func_declaration | binding_declaration | impl_declaration
|
||||||
//! ```
|
//! ```
|
||||||
@@ -153,6 +153,10 @@
|
|||||||
//! ```text
|
//! ```text
|
||||||
//! import := 'import' IDENTIFIER (:: IDENTIFIER)* import_suffix
|
//! import := 'import' IDENTIFIER (:: IDENTIFIER)* import_suffix
|
||||||
//! import_suffix := ε | '::{' IDENTIFIER (, IDENTIFIER)* '}' | '*' //TODO add qualified, exclusions, etc.
|
//! import_suffix := ε | '::{' IDENTIFIER (, IDENTIFIER)* '}' | '*' //TODO add qualified, exclusions, etc.
|
||||||
|
//!
|
||||||
|
//! ## Modules
|
||||||
|
//!
|
||||||
|
//! module := 'module' IDENTIFIER '{' statement* '}'
|
||||||
//! ```
|
//! ```
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
@@ -163,7 +167,9 @@ use crate::tokenizing::*;
|
|||||||
use crate::tokenizing::Kw::*;
|
use crate::tokenizing::Kw::*;
|
||||||
use crate::tokenizing::TokenKind::*;
|
use crate::tokenizing::TokenKind::*;
|
||||||
|
|
||||||
|
use crate::source_map::Location;
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
use crate::schala::SourceMapHandle;
|
||||||
|
|
||||||
/// Represents a parsing error
|
/// Represents a parsing error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -196,8 +202,10 @@ pub struct Parser {
|
|||||||
parse_level: u32,
|
parse_level: u32,
|
||||||
restrictions: ParserRestrictions,
|
restrictions: ParserRestrictions,
|
||||||
id_store: ItemIdStore,
|
id_store: ItemIdStore,
|
||||||
|
source_map: SourceMapHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ParserRestrictions {
|
struct ParserRestrictions {
|
||||||
no_struct_literal: bool
|
no_struct_literal: bool
|
||||||
}
|
}
|
||||||
@@ -205,14 +213,14 @@ struct ParserRestrictions {
|
|||||||
struct TokenHandler {
|
struct TokenHandler {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
end_of_file: (usize, usize),
|
end_of_file: Location
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenHandler {
|
impl TokenHandler {
|
||||||
fn new(tokens: Vec<Token>) -> TokenHandler {
|
fn new(tokens: Vec<Token>) -> TokenHandler {
|
||||||
let end_of_file = match tokens.last() {
|
let end_of_file = match tokens.last() {
|
||||||
None => (0, 0),
|
None => Location { line_num: 0, char_num : 0 },
|
||||||
Some(t) => (t.line_num, t.char_num)
|
Some(t) => t.location,
|
||||||
};
|
};
|
||||||
TokenHandler { idx: 0, tokens, end_of_file }
|
TokenHandler { idx: 0, tokens, end_of_file }
|
||||||
}
|
}
|
||||||
@@ -225,49 +233,56 @@ impl TokenHandler {
|
|||||||
self.peek_n(n).kind
|
self.peek_n(n).kind
|
||||||
}
|
}
|
||||||
fn peek(&mut self) -> Token {
|
fn peek(&mut self) -> Token {
|
||||||
self.tokens.get(self.idx).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, line_num: self.end_of_file.0, char_num: self.end_of_file.1})
|
self.tokens.get(self.idx).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
/// calling peek_n(0) is the same thing as peek()
|
/// calling peek_n(0) is the same thing as peek()
|
||||||
fn peek_n(&mut self, n: usize) -> Token {
|
fn peek_n(&mut self, n: usize) -> Token {
|
||||||
self.tokens.get(self.idx + n).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, line_num: self.end_of_file.0, char_num: self.end_of_file.1})
|
self.tokens.get(self.idx + n).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
fn next(&mut self) -> Token {
|
fn next(&mut self) -> Token {
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
self.tokens.get(self.idx - 1).map(|t: &Token| { t.clone() }).unwrap_or(Token { kind: TokenKind::EOF, line_num: self.end_of_file.0, char_num: self.end_of_file.1})
|
self.tokens.get(self.idx - 1).map(|t: &Token| { t.clone() }).unwrap_or(Token { kind: TokenKind::EOF, location: self.end_of_file })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
/// Create a new parser initialized with some tokens.
|
/// Create a new parser initialized with some tokens.
|
||||||
pub fn new(initial_input: Vec<Token>) -> Parser {
|
pub fn new(source_map: SourceMapHandle) -> Parser {
|
||||||
Parser {
|
Parser {
|
||||||
token_handler: TokenHandler::new(initial_input),
|
token_handler: TokenHandler::new(vec![]),
|
||||||
parse_record: vec![],
|
parse_record: vec![],
|
||||||
parse_level: 0,
|
parse_level: 0,
|
||||||
restrictions: ParserRestrictions { no_struct_literal: false },
|
restrictions: ParserRestrictions { no_struct_literal: false },
|
||||||
id_store: ItemIdStore::new(),
|
id_store: ItemIdStore::new(),
|
||||||
|
source_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_new_tokens(&mut self, new_tokens: Vec<Token>) {
|
||||||
|
self.token_handler = TokenHandler::new(new_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse all loaded tokens up to this point.
|
/// Parse all loaded tokens up to this point.
|
||||||
pub fn parse(&mut self) -> ParseResult<AST> {
|
pub fn parse(&mut self) -> ParseResult<AST> {
|
||||||
self.program()
|
self.program()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn parse_with_new_tokens(&mut self, new_tokens: Vec<Token>) -> ParseResult<AST> {
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn format_parse_trace(&self) -> String {
|
pub fn format_parse_trace(&self) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
buf.push_str("Parse productions:\n");
|
||||||
|
let mut next_token = None;
|
||||||
for r in self.parse_record.iter() {
|
for r in self.parse_record.iter() {
|
||||||
let mut indent = String::new();
|
let mut indent = String::new();
|
||||||
for _ in 0..r.level {
|
for _ in 0..r.level {
|
||||||
indent.push(' ');
|
indent.push('.');
|
||||||
}
|
}
|
||||||
buf.push_str(&format!("{}Production `{}`, token: {}\n", indent, r.production_name, r.next_token))
|
let effective_token = if next_token == Some(&r.next_token) {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
next_token = Some(&r.next_token);
|
||||||
|
format!(", next token: {}", r.next_token)
|
||||||
|
};
|
||||||
|
buf.push_str(&format!("{}`{}`{}\n", indent, r.production_name, effective_token));
|
||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
@@ -355,16 +370,20 @@ impl Parser {
|
|||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
fn statement(&mut self) -> ParseResult<Statement> {
|
fn statement(&mut self) -> ParseResult<Statement> {
|
||||||
//TODO handle error recovery here
|
//TODO handle error recovery here
|
||||||
let kind = match self.token_handler.peek().get_kind() {
|
let tok = self.token_handler.peek();
|
||||||
|
let kind = match tok.get_kind() {
|
||||||
Keyword(Type) => self.type_declaration().map(|decl| { StatementKind::Declaration(decl) }),
|
Keyword(Type) => self.type_declaration().map(|decl| { StatementKind::Declaration(decl) }),
|
||||||
Keyword(Func)=> self.func_declaration().map(|func| { StatementKind::Declaration(func) }),
|
Keyword(Func)=> self.func_declaration().map(|func| { StatementKind::Declaration(func) }),
|
||||||
Keyword(Let) => self.binding_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Let) => self.binding_declaration().map(|decl| StatementKind::Declaration(decl)),
|
||||||
Keyword(Interface) => self.interface_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Interface) => self.interface_declaration().map(|decl| StatementKind::Declaration(decl)),
|
||||||
Keyword(Impl) => self.impl_declaration().map(|decl| StatementKind::Declaration(decl)),
|
Keyword(Impl) => self.impl_declaration().map(|decl| StatementKind::Declaration(decl)),
|
||||||
Keyword(Import) => self.import_declaration().map(|spec| StatementKind::Import(spec)),
|
Keyword(Import) => self.import_declaration().map(|spec| StatementKind::Import(spec)),
|
||||||
|
Keyword(Module) => self.module_declaration().map(|spec| StatementKind::Module(spec)),
|
||||||
_ => self.expression().map(|expr| { StatementKind::Expression(expr) } ),
|
_ => self.expression().map(|expr| { StatementKind::Expression(expr) } ),
|
||||||
}?;
|
}?;
|
||||||
Ok(Statement { kind, id: self.id_store.fresh() })
|
let id = self.id_store.fresh();
|
||||||
|
self.source_map.borrow_mut().add_location(&id, tok.location);
|
||||||
|
Ok(Statement { kind, id })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
@@ -482,7 +501,6 @@ impl Parser {
|
|||||||
Ok(delimited!(self, LParen, formal_param, Comma, RParen))
|
Ok(delimited!(self, LParen, formal_param, Comma, RParen))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO needs to support default values
|
|
||||||
#[recursive_descent_method]
|
#[recursive_descent_method]
|
||||||
fn formal_param(&mut self) -> ParseResult<FormalParam> {
|
fn formal_param(&mut self) -> ParseResult<FormalParam> {
|
||||||
let name = self.identifier()?;
|
let name = self.identifier()?;
|
||||||
@@ -973,7 +991,7 @@ impl Parser {
|
|||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
Pattern::Literal(PatternLiteral::BoolPattern(false))
|
Pattern::Literal(PatternLiteral::BoolPattern(false))
|
||||||
},
|
},
|
||||||
StrLiteral(s) => {
|
StrLiteral { s, .. } => {
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
Pattern::Literal(PatternLiteral::StringPattern(s))
|
Pattern::Literal(PatternLiteral::StringPattern(s))
|
||||||
},
|
},
|
||||||
@@ -1122,7 +1140,7 @@ impl Parser {
|
|||||||
let id = self.id_store.fresh();
|
let id = self.id_store.fresh();
|
||||||
Ok(Expression::new(id, BoolLiteral(false)))
|
Ok(Expression::new(id, BoolLiteral(false)))
|
||||||
},
|
},
|
||||||
StrLiteral(s) => {
|
StrLiteral {s, ..} => {
|
||||||
self.token_handler.next();
|
self.token_handler.next();
|
||||||
let id = self.id_store.fresh();
|
let id = self.id_store.fresh();
|
||||||
Ok(Expression::new(id, StringLiteral(s.clone())))
|
Ok(Expression::new(id, StringLiteral(s.clone())))
|
||||||
@@ -1245,6 +1263,14 @@ impl Parser {
|
|||||||
_ => return ParseError::new_with_token("Expected '{{' or '*'", self.token_handler.peek()),
|
_ => return ParseError::new_with_token("Expected '{{' or '*'", self.token_handler.peek()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[recursive_descent_method]
|
||||||
|
fn module_declaration(&mut self) -> ParseResult<ModuleSpecifier> {
|
||||||
|
expect!(self, Keyword(Kw::Module));
|
||||||
|
let name = self.identifier()?;
|
||||||
|
let contents = delimited!(self, LCurlyBrace, statement, Newline | Semicolon, RCurlyBrace, nonstrict);
|
||||||
|
Ok(ModuleSpecifier { name, contents })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_binary(digits: String, tok: Token) -> ParseResult<u64> {
|
fn parse_binary(digits: String, tok: Token) -> ParseResult<u64> {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
use ::std::rc::Rc;
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::tokenize;
|
use super::{Parser, ParseResult, tokenize};
|
||||||
use super::ParseResult;
|
use crate::ast::*;
|
||||||
use crate::ast::{ItemIdStore, AST, Expression, Statement, StatementKind, IfExpressionBody, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName, ImportSpecifier, ImportedNames, Condition, ConditionArm};
|
|
||||||
use super::Declaration::*;
|
use super::Declaration::*;
|
||||||
use super::Signature;
|
use super::Signature;
|
||||||
use super::TypeIdentifier::*;
|
use super::TypeIdentifier::*;
|
||||||
@@ -13,9 +13,17 @@ use super::ExpressionKind::*;
|
|||||||
use super::Variant::*;
|
use super::Variant::*;
|
||||||
use super::ForBody::*;
|
use super::ForBody::*;
|
||||||
|
|
||||||
fn parse(input: &str) -> ParseResult<AST> {
|
fn make_parser(input: &str) -> Parser {
|
||||||
|
let source_map = crate::source_map::SourceMap::new();
|
||||||
|
let source_map_handle = Rc::new(RefCell::new(source_map));
|
||||||
let tokens: Vec<crate::tokenizing::Token> = tokenize(input);
|
let tokens: Vec<crate::tokenizing::Token> = tokenize(input);
|
||||||
let mut parser = super::Parser::new(tokens);
|
let mut parser = super::Parser::new(source_map_handle);
|
||||||
|
parser.add_new_tokens(tokens);
|
||||||
|
parser
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str) -> ParseResult<AST> {
|
||||||
|
let mut parser = make_parser(input);
|
||||||
parser.parse()
|
parser.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +71,18 @@ macro_rules! import {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! module {
|
||||||
|
($module_spec:expr) => {
|
||||||
|
Statement { id: ItemIdStore::new_id(), kind: StatementKind::Module($module_spec) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! ex {
|
macro_rules! ex {
|
||||||
($expr_type:expr) => { Expression::new(ItemIdStore::new_id(), $expr_type) };
|
($expr_type:expr) => { Expression::new(ItemIdStore::new_id(), $expr_type) };
|
||||||
($expr_type:expr, $type_anno:expr) => { Expression::with_anno(ItemIdStore::new_id(), $expr_type, $type_anno) };
|
($expr_type:expr, $type_anno:expr) => { Expression::with_anno(ItemIdStore::new_id(), $expr_type, $type_anno) };
|
||||||
(s $expr_text:expr) => {
|
(s $expr_text:expr) => {
|
||||||
{
|
{
|
||||||
let tokens: Vec<crate::tokenizing::Token> = tokenize($expr_text);
|
let mut parser = make_parser($expr_text);
|
||||||
let mut parser = super::Parser::new(tokens);
|
|
||||||
parser.expression().unwrap()
|
parser.expression().unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -92,8 +105,7 @@ macro_rules! exst {
|
|||||||
};
|
};
|
||||||
(s $statement_text:expr) => {
|
(s $statement_text:expr) => {
|
||||||
{
|
{
|
||||||
let tokens: Vec<crate::tokenizing::Token> = tokenize($statement_text);
|
let mut parser = make_parser($statement_text);
|
||||||
let mut parser = super::Parser::new(tokens);
|
|
||||||
parser.statement().unwrap()
|
parser.statement().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -796,3 +808,21 @@ fn if_expr() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn modules() {
|
||||||
|
parse_test_wrap_ast! {
|
||||||
|
r#"
|
||||||
|
module ephraim {
|
||||||
|
let a = 10
|
||||||
|
fn nah() { 33 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
module!(
|
||||||
|
ModuleSpecifier { name: rc!(ephraim), contents: vec![
|
||||||
|
decl!(Binding { name: rc!(a), constant: true, type_anno: None, expr: ex!(s "10") }),
|
||||||
|
decl!(FuncDecl(Signature { name: rc!(nah), operator: false, params: vec![], type_anno: None }, vec![exst!(NatLiteral(33))])),
|
||||||
|
] }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ type Ord = LT | EQ | GT
|
|||||||
|
|
||||||
fn map(input: Option<T>, func: Func): Option<T> {
|
fn map(input: Option<T>, func: Func): Option<T> {
|
||||||
if input {
|
if input {
|
||||||
is Option::Some(x) -> Option::Some(func(x)),
|
is Option::Some(x) then Option::Some(func(x)),
|
||||||
is Option::None -> Option::None,
|
is Option::None then Option::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ pub enum Stmt {
|
|||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Unit,
|
Unit,
|
||||||
Lit(Lit),
|
Lit(Lit),
|
||||||
|
Sym(Rc<String>), //a Sym is anything that can be looked up by name at runtime - i.e. a function or variable address
|
||||||
Tuple(Vec<Expr>),
|
Tuple(Vec<Expr>),
|
||||||
Func(Func),
|
Func(Func),
|
||||||
Sym(Rc<String>),
|
|
||||||
Constructor {
|
Constructor {
|
||||||
type_name: Rc<String>,
|
type_name: Rc<String>,
|
||||||
name: Rc<String>,
|
name: Rc<String>,
|
||||||
@@ -56,7 +56,7 @@ pub enum Expr {
|
|||||||
args: Vec<Expr>,
|
args: Vec<Expr>,
|
||||||
},
|
},
|
||||||
Assign {
|
Assign {
|
||||||
val: Box<Expr>,
|
val: Box<Expr>, //TODO this probably can't be a val
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
},
|
},
|
||||||
Conditional {
|
Conditional {
|
||||||
@@ -131,6 +131,12 @@ impl<'a> Reducer<'a> {
|
|||||||
StatementKind::Expression(expr) => Stmt::Expr(self.expression(&expr)),
|
StatementKind::Expression(expr) => Stmt::Expr(self.expression(&expr)),
|
||||||
StatementKind::Declaration(decl) => self.declaration(&decl),
|
StatementKind::Declaration(decl) => self.declaration(&decl),
|
||||||
StatementKind::Import(_) => Stmt::Noop,
|
StatementKind::Import(_) => Stmt::Noop,
|
||||||
|
StatementKind::Module(modspec) => {
|
||||||
|
for statement in modspec.contents.iter() {
|
||||||
|
self.statement(&statement);
|
||||||
|
}
|
||||||
|
Stmt::Noop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,25 +164,7 @@ impl<'a> Reducer<'a> {
|
|||||||
BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)),
|
BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)),
|
||||||
BinExp(binop, lhs, rhs) => self.binop(binop, lhs, rhs),
|
BinExp(binop, lhs, rhs) => self.binop(binop, lhs, rhs),
|
||||||
PrefixExp(op, arg) => self.prefix(op, arg),
|
PrefixExp(op, arg) => self.prefix(op, arg),
|
||||||
Value(qualified_name) => {
|
Value(qualified_name) => self.value(qualified_name),
|
||||||
let ref id = qualified_name.id;
|
|
||||||
let ref sym_name = match symbol_table.get_fqsn_from_id(id) {
|
|
||||||
Some(fqsn) => fqsn,
|
|
||||||
None => return Expr::ReductionError(format!("FQSN lookup for Value {:?} failed", qualified_name)),
|
|
||||||
};
|
|
||||||
//TODO this probably needs to change
|
|
||||||
let FullyQualifiedSymbolName(ref v) = sym_name;
|
|
||||||
let name = v.last().unwrap().name.clone();
|
|
||||||
match symbol_table.lookup_by_fqsn(&sym_name) {
|
|
||||||
Some(Symbol { spec: SymbolSpec::DataConstructor { index, type_args, type_name}, .. }) => Expr::Constructor {
|
|
||||||
type_name: type_name.clone(),
|
|
||||||
name: name.clone(),
|
|
||||||
tag: index.clone(),
|
|
||||||
arity: type_args.len(),
|
|
||||||
},
|
|
||||||
_ => Expr::Sym(name.clone()),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Call { f, arguments } => self.reduce_call_expression(f, arguments),
|
Call { f, arguments } => self.reduce_call_expression(f, arguments),
|
||||||
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| self.expression(e)).collect()),
|
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| self.expression(e)).collect()),
|
||||||
IfExpression { discriminator, body } => self.reduce_if_expression(deref_optional_box(discriminator), body),
|
IfExpression { discriminator, body } => self.reduce_if_expression(deref_optional_box(discriminator), body),
|
||||||
@@ -189,6 +177,38 @@ impl<'a> Reducer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value(&mut self, qualified_name: &QualifiedName) -> Expr {
|
||||||
|
let symbol_table = self.symbol_table;
|
||||||
|
let ref id = qualified_name.id;
|
||||||
|
let ref sym_name = match symbol_table.get_fqsn_from_id(id) {
|
||||||
|
Some(fqsn) => fqsn,
|
||||||
|
None => return Expr::ReductionError(format!("FQSN lookup for Value {:?} failed", qualified_name)),
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO this probably needs to change
|
||||||
|
let FullyQualifiedSymbolName(ref v) = sym_name;
|
||||||
|
let name = v.last().unwrap().name.clone();
|
||||||
|
|
||||||
|
let Symbol { local_name, spec, .. } = match symbol_table.lookup_by_fqsn(&sym_name) {
|
||||||
|
Some(s) => s,
|
||||||
|
//None => return Expr::ReductionError(format!("Symbol {:?} not found", sym_name)),
|
||||||
|
None => return Expr::Sym(name.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
match spec {
|
||||||
|
SymbolSpec::RecordConstructor { .. } => Expr::ReductionError(format!("AST reducer doesn't expect a RecordConstructor here")),
|
||||||
|
SymbolSpec::DataConstructor { index, type_args, type_name } => Expr::Constructor {
|
||||||
|
type_name: type_name.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
tag: index.clone(),
|
||||||
|
arity: type_args.len(),
|
||||||
|
},
|
||||||
|
SymbolSpec::Func(_) => Expr::Sym(local_name.clone()),
|
||||||
|
SymbolSpec::Binding => Expr::Sym(local_name.clone()), //TODO not sure if this is right, probably needs to eventually be fqsn
|
||||||
|
SymbolSpec::Type { .. } => Expr::ReductionError("AST reducer doesnt expect a type here".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn reduce_lambda(&mut self, params: &Vec<FormalParam>, body: &Block) -> Expr {
|
fn reduce_lambda(&mut self, params: &Vec<FormalParam>, body: &Block) -> Expr {
|
||||||
Expr::Func(Func::UserDefined {
|
Expr::Func(Func::UserDefined {
|
||||||
name: None,
|
name: None,
|
||||||
@@ -444,7 +464,6 @@ impl Pattern {
|
|||||||
VarOrName(QualifiedName { components, id }) => {
|
VarOrName(QualifiedName { components, id }) => {
|
||||||
// if fqsn is Some, treat this as a symbol pattern. If it's None, treat it
|
// if fqsn is Some, treat this as a symbol pattern. If it's None, treat it
|
||||||
// as a variable.
|
// as a variable.
|
||||||
println!("Calling VarOrName reduction with : {:?}", components);
|
|
||||||
let fqsn = symbol_table.get_fqsn_from_id(&id);
|
let fqsn = symbol_table.get_fqsn_from_id(&id);
|
||||||
match fqsn.and_then(|fqsn| symbol_table.lookup_by_fqsn(&fqsn)) {
|
match fqsn.and_then(|fqsn| symbol_table.lookup_by_fqsn(&fqsn)) {
|
||||||
Some(symbol) => handle_symbol(Some(symbol), &vec![], symbol_table),
|
Some(symbol) => handle_symbol(Some(symbol), &vec![], symbol_table),
|
||||||
|
|||||||
@@ -10,17 +10,22 @@ use schala_repl::{ProgrammingLanguageInterface,
|
|||||||
ComputationRequest, ComputationResponse,
|
ComputationRequest, ComputationResponse,
|
||||||
LangMetaRequest, LangMetaResponse, GlobalOutputStats,
|
LangMetaRequest, LangMetaResponse, GlobalOutputStats,
|
||||||
DebugResponse, DebugAsk};
|
DebugResponse, DebugAsk};
|
||||||
use crate::{ast, reduced_ast, tokenizing, parsing, eval, typechecking, symbol_table};
|
use crate::{ast, reduced_ast, tokenizing, parsing, eval, typechecking, symbol_table, source_map};
|
||||||
|
|
||||||
|
pub type SymbolTableHandle = Rc<RefCell<symbol_table::SymbolTable>>;
|
||||||
|
pub type SourceMapHandle = Rc<RefCell<source_map::SourceMap>>;
|
||||||
|
|
||||||
/// All the state necessary to parse and execute a Schala program are stored in this struct.
|
/// All the state necessary to parse and execute a Schala program are stored in this struct.
|
||||||
/// `state` represents the execution state for the AST-walking interpreter, the other fields
|
/// `state` represents the execution state for the AST-walking interpreter, the other fields
|
||||||
/// should be self-explanatory.
|
/// should be self-explanatory.
|
||||||
pub struct Schala {
|
pub struct Schala {
|
||||||
source_reference: SourceReference,
|
source_reference: SourceReference,
|
||||||
|
source_map: SourceMapHandle,
|
||||||
state: eval::State<'static>,
|
state: eval::State<'static>,
|
||||||
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
|
symbol_table: SymbolTableHandle,
|
||||||
|
resolver: crate::scope_resolution::ScopeResolver<'static>,
|
||||||
type_context: typechecking::TypeContext<'static>,
|
type_context: typechecking::TypeContext<'static>,
|
||||||
active_parser: Option<parsing::Parser>,
|
active_parser: parsing::Parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schala {
|
impl Schala {
|
||||||
@@ -34,13 +39,17 @@ impl Schala {
|
|||||||
impl Schala {
|
impl Schala {
|
||||||
/// Creates a new Schala environment *without* any prelude.
|
/// Creates a new Schala environment *without* any prelude.
|
||||||
fn new_blank_env() -> Schala {
|
fn new_blank_env() -> Schala {
|
||||||
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
|
let source_map = Rc::new(RefCell::new(source_map::SourceMap::new()));
|
||||||
|
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new(source_map.clone())));
|
||||||
Schala {
|
Schala {
|
||||||
|
//TODO maybe these can be the same structure
|
||||||
source_reference: SourceReference::new(),
|
source_reference: SourceReference::new(),
|
||||||
symbol_table: symbols.clone(),
|
symbol_table: symbols.clone(),
|
||||||
state: eval::State::new(symbols),
|
source_map: source_map.clone(),
|
||||||
|
resolver: crate::scope_resolution::ScopeResolver::new(symbols.clone()),
|
||||||
|
state: eval::State::new(),
|
||||||
type_context: typechecking::TypeContext::new(),
|
type_context: typechecking::TypeContext::new(),
|
||||||
active_parser: None,
|
active_parser: parsing::Parser::new(source_map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +60,10 @@ impl Schala {
|
|||||||
let mut s = Schala::new_blank_env();
|
let mut s = Schala::new_blank_env();
|
||||||
|
|
||||||
let request = ComputationRequest { source: prelude, debug_requests: HashSet::default() };
|
let request = ComputationRequest { source: prelude, debug_requests: HashSet::default() };
|
||||||
s.run_computation(request);
|
let response = s.run_computation(request);
|
||||||
|
if let Err(msg) = response.main_output {
|
||||||
|
panic!("Error in prelude, panicking: {}", msg);
|
||||||
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,10 +106,10 @@ fn tokenizing(input: &str, _handle: &mut Schala, comp: Option<&mut PassDebugArti
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
|
fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
|
||||||
use crate::parsing::Parser;
|
|
||||||
use ParsingDebugType::*;
|
use ParsingDebugType::*;
|
||||||
|
|
||||||
let mut parser = handle.active_parser.take().unwrap_or_else(|| Parser::new(input));
|
let ref mut parser = handle.active_parser;
|
||||||
|
parser.add_new_tokens(input);
|
||||||
let ast = parser.parse();
|
let ast = parser.parse();
|
||||||
|
|
||||||
comp.map(|comp| {
|
comp.map(|comp| {
|
||||||
@@ -115,13 +127,13 @@ fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut
|
|||||||
};
|
};
|
||||||
comp.add_artifact(debug_info);
|
comp.add_artifact(debug_info);
|
||||||
});
|
});
|
||||||
ast.map_err(|err| format_parse_error(err, handle))
|
ast.map_err(|err| format_parse_error(err, &handle.source_reference))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_parse_error(error: parsing::ParseError, handle: &mut Schala) -> String {
|
fn format_parse_error(error: parsing::ParseError, source_reference: &SourceReference) -> String {
|
||||||
let line_num = error.token.line_num;
|
let line_num = error.token.location.line_num;
|
||||||
let ch = error.token.char_num;
|
let ch = error.token.location.char_num;
|
||||||
let line_from_program = handle.source_reference.get_line(line_num);
|
let line_from_program = source_reference.get_line(line_num);
|
||||||
let location_pointer = format!("{}^", " ".repeat(ch));
|
let location_pointer = format!("{}^", " ".repeat(ch));
|
||||||
|
|
||||||
let line_num_digits = format!("{}", line_num).chars().count();
|
let line_num_digits = format!("{}", line_num).chars().count();
|
||||||
@@ -151,9 +163,7 @@ fn symbol_table(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scope_resolution(mut input: ast::AST, handle: &mut Schala, _com: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
|
fn scope_resolution(mut input: ast::AST, handle: &mut Schala, _com: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
|
||||||
let mut symbol_table = handle.symbol_table.borrow_mut();
|
let () = handle.resolver.resolve(&mut input)?;
|
||||||
let mut resolver = crate::scope_resolution::ScopeResolver::new(&mut symbol_table);
|
|
||||||
let () = resolver.resolve(&mut input)?;
|
|
||||||
Ok(input)
|
Ok(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,210 +1,111 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::symbol_table::{SymbolTable, ScopeSegment, ScopeSegmentKind, FullyQualifiedSymbolName};
|
use crate::schala::SymbolTableHandle;
|
||||||
|
use crate::symbol_table::{ScopeSegment, FullyQualifiedSymbolName};
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::util::ScopeStack;
|
use crate::util::ScopeStack;
|
||||||
|
|
||||||
type FQSNPrefix = Vec<ScopeSegment>;
|
type FQSNPrefix = Vec<ScopeSegment>;
|
||||||
type NameScopeStack<'t> = ScopeStack<'t, Rc<String>, FQSNPrefix>;
|
|
||||||
|
|
||||||
pub struct ScopeResolver<'a> {
|
pub struct ScopeResolver<'a> {
|
||||||
symbol_table: &'a mut SymbolTable,
|
symbol_table_handle: SymbolTableHandle,
|
||||||
name_scope_stack: ScopeStack<'a, Rc<String>, FQSNPrefix>,
|
name_scope_stack: ScopeStack<'a, Rc<String>, FQSNPrefix>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ASTVisitor for ScopeResolver<'a> {
|
||||||
impl<'a> ScopeResolver<'a> {
|
//TODO need to un-insert these - maybe need to rethink visitor
|
||||||
pub fn new(symbol_table: &'a mut SymbolTable) -> ScopeResolver {
|
fn import(&mut self, import_spec: &ImportSpecifier) {
|
||||||
let name_scope_stack = ScopeStack::new(None);
|
let ref symbol_table = self.symbol_table_handle.borrow();
|
||||||
ScopeResolver { symbol_table, name_scope_stack }
|
|
||||||
}
|
|
||||||
pub fn resolve(&mut self, ast: &mut AST) -> Result<(), String> {
|
|
||||||
self.block(&mut ast.statements)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//TODO this is incomplete
|
|
||||||
fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> FullyQualifiedSymbolName {
|
|
||||||
let QualifiedName { components: vec, .. } = sym_name;
|
|
||||||
let len = vec.len();
|
|
||||||
let new_vec: Vec<ScopeSegment> = vec.iter().enumerate().map(|(i, name)| {
|
|
||||||
let kind = if i == (len - 1) {
|
|
||||||
ScopeSegmentKind::Terminal
|
|
||||||
} else {
|
|
||||||
ScopeSegmentKind::Type
|
|
||||||
};
|
|
||||||
ScopeSegment { name: name.clone(), kind }
|
|
||||||
}).collect();
|
|
||||||
FullyQualifiedSymbolName(new_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import(&mut self, import_spec: &ImportSpecifier) -> Result<(), String> {
|
|
||||||
let ImportSpecifier { ref path_components, ref imported_names, .. } = &import_spec;
|
let ImportSpecifier { ref path_components, ref imported_names, .. } = &import_spec;
|
||||||
match imported_names {
|
match imported_names {
|
||||||
ImportedNames::All => unimplemented!(),
|
ImportedNames::All => {
|
||||||
|
let prefix = FullyQualifiedSymbolName(path_components.iter().map(|c| ScopeSegment {
|
||||||
|
name: c.clone(),
|
||||||
|
}).collect());
|
||||||
|
let members = symbol_table.lookup_children_of_fqsn(&prefix);
|
||||||
|
for member in members.into_iter() {
|
||||||
|
let local_name = member.0.last().unwrap().name.clone();
|
||||||
|
self.name_scope_stack.insert(local_name.clone(), member.0);
|
||||||
|
}
|
||||||
|
},
|
||||||
ImportedNames::LastOfPath => {
|
ImportedNames::LastOfPath => {
|
||||||
let name = path_components.last().unwrap(); //TODO handle better
|
let name = path_components.last().unwrap(); //TODO handle better
|
||||||
let fqsn_prefix = path_components.iter().map(|c| ScopeSegment {
|
let fqsn_prefix = path_components.iter().map(|c| ScopeSegment {
|
||||||
name: c.clone(), kind: ScopeSegmentKind::Type
|
name: c.clone(),
|
||||||
}).collect();
|
}).collect();
|
||||||
self.name_scope_stack.insert(name.clone(), fqsn_prefix);
|
self.name_scope_stack.insert(name.clone(), fqsn_prefix);
|
||||||
()
|
|
||||||
}
|
}
|
||||||
ImportedNames::List(ref names) => {
|
ImportedNames::List(ref names) => {
|
||||||
let fqsn_prefix: FQSNPrefix = path_components.iter().map(|c| ScopeSegment {
|
let fqsn_prefix: FQSNPrefix = path_components.iter().map(|c| ScopeSegment {
|
||||||
name: c.clone(), kind: ScopeSegmentKind::Type
|
name: c.clone(),
|
||||||
}).collect();
|
}).collect();
|
||||||
for name in names.iter() {
|
for name in names.iter() {
|
||||||
self.name_scope_stack.insert(name.clone(), fqsn_prefix.clone());
|
self.name_scope_stack.insert(name.clone(), fqsn_prefix.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decl(&mut self, decl: &Declaration) -> Result<(), String> {
|
fn qualified_name(&mut self, qualified_name: &QualifiedName) {
|
||||||
use Declaration::*;
|
let ref mut symbol_table = self.symbol_table_handle.borrow_mut();
|
||||||
match decl {
|
let fqsn = self.lookup_name_in_scope(&qualified_name);
|
||||||
Binding { expr, .. } => self.expr(expr),
|
let ref id = qualified_name.id;
|
||||||
FuncDecl(_, block) => self.block(block),
|
symbol_table.map_id_to_fqsn(id, fqsn);
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn block(&mut self, block: &Block) -> Result<(), String> {
|
|
||||||
for statement in block.iter() {
|
|
||||||
match statement.kind {
|
|
||||||
StatementKind::Declaration(ref decl) => self.decl(decl),
|
|
||||||
StatementKind::Expression(ref expr) => self.expr(expr),
|
|
||||||
StatementKind::Import(ref spec) => self.import(spec),
|
|
||||||
}?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr(&mut self, expr: &Expression) -> Result<(), String> {
|
fn named_struct(&mut self, name: &QualifiedName, _fields: &Vec<(Rc<String>, Expression)>) {
|
||||||
use ExpressionKind::*;
|
let ref mut symbol_table = self.symbol_table_handle.borrow_mut();
|
||||||
match &expr.kind {
|
let ref id = name.id;
|
||||||
ExpressionKind::Value(qualified_name) => {
|
let fqsn = self.lookup_name_in_scope(&name);
|
||||||
let fqsn = self.lookup_name_in_scope(&qualified_name);
|
symbol_table.map_id_to_fqsn(id, fqsn);
|
||||||
let ref id = qualified_name.id;
|
|
||||||
self.symbol_table.map_id_to_fqsn(id, fqsn);
|
|
||||||
},
|
|
||||||
NamedStruct { name, .. } => {
|
|
||||||
let ref id = name.id;
|
|
||||||
let fqsn = self.lookup_name_in_scope(&name);
|
|
||||||
self.symbol_table.map_id_to_fqsn(id, fqsn);
|
|
||||||
},
|
|
||||||
BinExp(_, ref lhs, ref rhs) => {
|
|
||||||
self.expr(lhs)?;
|
|
||||||
self.expr(rhs)?;
|
|
||||||
},
|
|
||||||
PrefixExp(_, ref arg) => {
|
|
||||||
self.expr(arg)?;
|
|
||||||
},
|
|
||||||
TupleLiteral(exprs) => {
|
|
||||||
for expr in exprs.iter() {
|
|
||||||
self.expr(expr)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Call { f, arguments } => {
|
|
||||||
self.expr(&f)?;
|
|
||||||
for arg in arguments.iter() {
|
|
||||||
self.invoc(arg)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IfExpression { discriminator, body } => {
|
|
||||||
match discriminator {
|
|
||||||
None => (),
|
|
||||||
Some(ref expr) => self.expr(expr)?
|
|
||||||
};
|
|
||||||
self.if_expr_body(body)?;
|
|
||||||
},
|
|
||||||
Lambda { params, body, .. } => {
|
|
||||||
self.block(&body)?;
|
|
||||||
for param in params.iter() {
|
|
||||||
if let Some(ref expr) = param.default {
|
|
||||||
self.expr(expr)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invoc(&mut self, invoc: &InvocationArgument) -> Result<(), String> {
|
fn pattern(&mut self, pat: &Pattern) {
|
||||||
use InvocationArgument::*;
|
|
||||||
match invoc {
|
|
||||||
Positional(expr) => self.expr(expr),
|
|
||||||
Keyword { expr, .. } => self.expr(expr),
|
|
||||||
_ => Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn if_expr_body(&mut self, body: &IfExpressionBody) -> Result<(), String> {
|
|
||||||
use IfExpressionBody::*;
|
|
||||||
match body {
|
|
||||||
SimpleConditional { then_case, else_case } => {
|
|
||||||
self.block(then_case)?;
|
|
||||||
if let Some(block) = else_case {
|
|
||||||
self.block(block)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SimplePatternMatch { pattern, then_case, else_case } => {
|
|
||||||
self.pattern(pattern)?;
|
|
||||||
self.block(then_case)?;
|
|
||||||
if let Some(block) = else_case {
|
|
||||||
self.block(block)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CondList(conditions) => {
|
|
||||||
for arm in conditions.iter() {
|
|
||||||
if let Condition::Pattern(ref pat) = arm.condition {
|
|
||||||
self.pattern(pat)?;
|
|
||||||
}
|
|
||||||
self.block(&arm.body)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pattern(&mut self, pat: &Pattern) -> Result<(), String> {
|
|
||||||
use Pattern::*;
|
use Pattern::*;
|
||||||
match pat {
|
match pat {
|
||||||
Ignored => (),
|
Ignored => (),
|
||||||
TuplePattern(patterns) => {
|
TuplePattern(_) => (),
|
||||||
for pat in patterns {
|
|
||||||
self.pattern(pat)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Literal(_) => (),
|
Literal(_) => (),
|
||||||
TupleStruct(name, patterns) => {
|
TupleStruct(name, _) => self.qualified_name_in_pattern(name),
|
||||||
self.qualified_name_in_pattern(name);
|
Record(name, _) => self.qualified_name_in_pattern(name),
|
||||||
for pat in patterns {
|
VarOrName(name) => self.qualified_name_in_pattern(name),
|
||||||
self.pattern(pat)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Record(name, key_patterns) => {
|
|
||||||
self.qualified_name_in_pattern(name);
|
|
||||||
for (_, pat) in key_patterns {
|
|
||||||
self.pattern(pat)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
VarOrName(name) => {
|
|
||||||
self.qualified_name_in_pattern(name);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ScopeResolver<'a> {
|
||||||
|
pub fn new(symbol_table_handle: SymbolTableHandle) -> ScopeResolver<'static> {
|
||||||
|
let name_scope_stack = ScopeStack::new(None);
|
||||||
|
ScopeResolver { symbol_table_handle, name_scope_stack }
|
||||||
|
}
|
||||||
|
pub fn resolve(&mut self, ast: &mut AST) -> Result<(), String> {
|
||||||
|
walk_ast(self, ast);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> FullyQualifiedSymbolName {
|
||||||
|
let QualifiedName { components, .. } = sym_name;
|
||||||
|
let first_component = &components[0];
|
||||||
|
match self.name_scope_stack.lookup(first_component) {
|
||||||
|
None => {
|
||||||
|
FullyQualifiedSymbolName(components.iter().map(|name| ScopeSegment { name: name.clone() }).collect())
|
||||||
|
},
|
||||||
|
Some(fqsn_prefix) => {
|
||||||
|
let mut full_name = fqsn_prefix.clone();
|
||||||
|
let rest_of_name: FQSNPrefix = components[1..].iter().map(|name| ScopeSegment { name: name.clone() }).collect();
|
||||||
|
full_name.extend_from_slice(&rest_of_name);
|
||||||
|
FullyQualifiedSymbolName(full_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// this might be a variable or a pattern. if a variable, set to none
|
/// this might be a variable or a pattern. if a variable, set to none
|
||||||
fn qualified_name_in_pattern(&mut self, qualified_name: &QualifiedName) {
|
fn qualified_name_in_pattern(&mut self, qualified_name: &QualifiedName) {
|
||||||
|
let ref mut symbol_table = self.symbol_table_handle.borrow_mut();
|
||||||
let ref id = qualified_name.id;
|
let ref id = qualified_name.id;
|
||||||
let fqsn = self.lookup_name_in_scope(qualified_name);
|
let fqsn = self.lookup_name_in_scope(qualified_name);
|
||||||
if self.symbol_table.lookup_by_fqsn(&fqsn).is_some() {
|
if symbol_table.lookup_by_fqsn(&fqsn).is_some() {
|
||||||
self.symbol_table.map_id_to_fqsn(&id, fqsn);
|
symbol_table.map_id_to_fqsn(&id, fqsn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
schala-lang/language/src/source_map.rs
Normal file
39
schala-lang/language/src/source_map.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::ast::ItemId;
|
||||||
|
|
||||||
|
pub type LineNumber = usize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Location {
|
||||||
|
pub line_num: LineNumber,
|
||||||
|
pub char_num: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Location {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}:{}", self.line_num, self.char_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SourceMap {
|
||||||
|
map: HashMap<ItemId, Location>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceMap {
|
||||||
|
pub fn new() -> SourceMap {
|
||||||
|
SourceMap { map: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_location(&mut self, id: &ItemId, loc: Location) {
|
||||||
|
self.map.insert(id.clone(), loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&self, id: &ItemId) -> Option<Location> {
|
||||||
|
match self.map.get(id) {
|
||||||
|
Some(loc) => Some(loc.clone()),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,59 @@ use std::rc::Rc;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use crate::schala::SourceMapHandle;
|
||||||
|
use crate::source_map::{SourceMap, LineNumber};
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
use crate::ast::{ItemId, TypeBody, TypeSingletonName, Signature, Statement, StatementKind};
|
use crate::ast::{ItemId, TypeBody, TypeSingletonName, Signature, Statement, StatementKind, ModuleSpecifier};
|
||||||
use crate::typechecking::TypeName;
|
use crate::typechecking::TypeName;
|
||||||
|
|
||||||
type LineNumber = u32;
|
|
||||||
type SymbolTrackTable = HashMap<Rc<String>, LineNumber>;
|
#[allow(unused_macros)]
|
||||||
|
macro_rules! fqsn {
|
||||||
|
( $( $name:expr ; $kind:tt),* ) => {
|
||||||
|
{
|
||||||
|
let mut vec = vec![];
|
||||||
|
$(
|
||||||
|
vec.push(crate::symbol_table::ScopeSegment::new(std::rc::Rc::new($name.to_string())));
|
||||||
|
)*
|
||||||
|
FullyQualifiedSymbolName(vec)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mod symbol_trie;
|
||||||
|
use symbol_trie::SymbolTrie;
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
/// Keeps track of what names were used in a given namespace. Call try_register to add a name to
|
||||||
|
/// the table, or report an error if a name already exists.
|
||||||
|
struct DuplicateNameTrackTable {
|
||||||
|
table: HashMap<Rc<String>, LineNumber>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DuplicateNameTrackTable {
|
||||||
|
fn new() -> DuplicateNameTrackTable {
|
||||||
|
DuplicateNameTrackTable { table: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_register(&mut self, name: &Rc<String>, id: &ItemId, source_map: &SourceMap) -> Result<(), LineNumber> {
|
||||||
|
match self.table.entry(name.clone()) {
|
||||||
|
Entry::Occupied(o) => {
|
||||||
|
let line_number = o.get();
|
||||||
|
Err(*line_number)
|
||||||
|
},
|
||||||
|
Entry::Vacant(v) => {
|
||||||
|
let line_number = if let Some(loc) = source_map.lookup(id) {
|
||||||
|
loc.line_num
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
v.insert(line_number);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
|
||||||
pub struct FullyQualifiedSymbolName(pub Vec<ScopeSegment>);
|
pub struct FullyQualifiedSymbolName(pub Vec<ScopeSegment>);
|
||||||
@@ -27,71 +74,37 @@ impl fmt::Display for FullyQualifiedSymbolName {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ScopeSegment {
|
pub struct ScopeSegment {
|
||||||
pub name: Rc<String>, //TODO maybe this could be a &str, for efficiency?
|
pub name: Rc<String>, //TODO maybe this could be a &str, for efficiency?
|
||||||
pub kind: ScopeSegmentKind,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScopeSegment {
|
impl fmt::Display for ScopeSegment {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use colored::*;
|
let kind = ""; //TODO implement some kind of kind-tracking here
|
||||||
use ScopeSegmentKind::*;
|
|
||||||
let kind = match self.kind {
|
|
||||||
Function => "[fn]".green(),
|
|
||||||
Type => "[ty]".red(),
|
|
||||||
Terminal => "[tr]".blue(),
|
|
||||||
};
|
|
||||||
write!(f, "{}{}", self.name, kind)
|
write!(f, "{}{}", self.name, kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeSegment {
|
impl ScopeSegment {
|
||||||
pub fn new(name: Rc<String>, kind: ScopeSegmentKind) -> ScopeSegment {
|
pub fn new(name: Rc<String>) -> ScopeSegment {
|
||||||
ScopeSegment { name, kind }
|
ScopeSegment { name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
|
||||||
pub enum ScopeSegmentKind {
|
|
||||||
Function,
|
|
||||||
Type,
|
|
||||||
Terminal,
|
|
||||||
//Module
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! fqsn {
|
|
||||||
( $( $name:expr ; $kind:tt),* ) => {
|
|
||||||
{
|
|
||||||
let mut vec = vec![];
|
|
||||||
$(
|
|
||||||
vec.push(ScopeSegment::new(
|
|
||||||
Rc::new($name.to_string()),
|
|
||||||
sym_path_kind!($kind),
|
|
||||||
));
|
|
||||||
)*
|
|
||||||
FullyQualifiedSymbolName(vec)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! sym_path_kind {
|
|
||||||
(fn) => { ScopeSegmentKind::Function };
|
|
||||||
(ty) => { ScopeSegmentKind::Type };
|
|
||||||
(tr) => { ScopeSegmentKind::Terminal };
|
|
||||||
}
|
|
||||||
|
|
||||||
//cf. p. 150 or so of Language Implementation Patterns
|
//cf. p. 150 or so of Language Implementation Patterns
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
|
source_map_handle: SourceMapHandle,
|
||||||
symbol_path_to_symbol: HashMap<FullyQualifiedSymbolName, Symbol>,
|
symbol_path_to_symbol: HashMap<FullyQualifiedSymbolName, Symbol>,
|
||||||
id_to_fqsn: HashMap<ItemId, FullyQualifiedSymbolName>,
|
id_to_fqsn: HashMap<ItemId, FullyQualifiedSymbolName>,
|
||||||
|
symbol_trie: SymbolTrie,
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add various types of lookups here, maybe multiple hash tables internally?
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
pub fn new() -> SymbolTable {
|
pub fn new(source_map_handle: SourceMapHandle) -> SymbolTable {
|
||||||
SymbolTable {
|
SymbolTable {
|
||||||
|
source_map_handle,
|
||||||
symbol_path_to_symbol: HashMap::new(),
|
symbol_path_to_symbol: HashMap::new(),
|
||||||
id_to_fqsn: HashMap::new(),
|
id_to_fqsn: HashMap::new(),
|
||||||
|
symbol_trie: SymbolTrie::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,15 +118,20 @@ impl SymbolTable {
|
|||||||
|
|
||||||
fn add_new_symbol(&mut self, local_name: &Rc<String>, scope_path: &Vec<ScopeSegment>, spec: SymbolSpec) {
|
fn add_new_symbol(&mut self, local_name: &Rc<String>, scope_path: &Vec<ScopeSegment>, spec: SymbolSpec) {
|
||||||
let mut vec: Vec<ScopeSegment> = scope_path.clone();
|
let mut vec: Vec<ScopeSegment> = scope_path.clone();
|
||||||
vec.push(ScopeSegment { name: local_name.clone(), kind: ScopeSegmentKind::Terminal });
|
vec.push(ScopeSegment { name: local_name.clone() });
|
||||||
let fully_qualified_name = FullyQualifiedSymbolName(vec);
|
let fully_qualified_name = FullyQualifiedSymbolName(vec);
|
||||||
let symbol = Symbol { local_name: local_name.clone(), fully_qualified_name: fully_qualified_name.clone(), spec };
|
let symbol = Symbol { local_name: local_name.clone(), fully_qualified_name: fully_qualified_name.clone(), spec };
|
||||||
|
self.symbol_trie.insert(&fully_qualified_name);
|
||||||
self.symbol_path_to_symbol.insert(fully_qualified_name, symbol);
|
self.symbol_path_to_symbol.insert(fully_qualified_name, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_by_fqsn(&self, fully_qualified_path: &FullyQualifiedSymbolName) -> Option<&Symbol> {
|
pub fn lookup_by_fqsn(&self, fully_qualified_path: &FullyQualifiedSymbolName) -> Option<&Symbol> {
|
||||||
self.symbol_path_to_symbol.get(fully_qualified_path)
|
self.symbol_path_to_symbol.get(fully_qualified_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lookup_children_of_fqsn(&self, path: &FullyQualifiedSymbolName) -> Vec<FullyQualifiedSymbolName> {
|
||||||
|
self.symbol_trie.get_children(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -142,7 +160,10 @@ pub enum SymbolSpec {
|
|||||||
members: HashMap<Rc<String>, TypeName>,
|
members: HashMap<Rc<String>, TypeName>,
|
||||||
type_name: TypeName,
|
type_name: TypeName,
|
||||||
},
|
},
|
||||||
Binding
|
Binding,
|
||||||
|
Type {
|
||||||
|
name: TypeName
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SymbolSpec {
|
impl fmt::Display for SymbolSpec {
|
||||||
@@ -153,6 +174,7 @@ impl fmt::Display for SymbolSpec {
|
|||||||
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor(idx: {})({:?} -> {})", index, type_args, type_name),
|
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor(idx: {})({:?} -> {})", index, type_args, type_name),
|
||||||
RecordConstructor { type_name, index, ..} => write!(f, "RecordConstructor(idx: {})(<members> -> {})", index, type_name),
|
RecordConstructor { type_name, index, ..} => write!(f, "RecordConstructor(idx: {})(<members> -> {})", index, type_name),
|
||||||
Binding => write!(f, "Binding"),
|
Binding => write!(f, "Binding"),
|
||||||
|
Type { name } => write!(f, "Type <{}>", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,51 +191,51 @@ impl SymbolTable {
|
|||||||
fn add_symbols_from_scope<'a>(&'a mut self, statements: &Vec<Statement>, scope_name_stack: &mut Vec<ScopeSegment>) -> Result<(), String> {
|
fn add_symbols_from_scope<'a>(&'a mut self, statements: &Vec<Statement>, scope_name_stack: &mut Vec<ScopeSegment>) -> Result<(), String> {
|
||||||
use self::ast::Declaration::*;
|
use self::ast::Declaration::*;
|
||||||
|
|
||||||
fn insert_and_check_duplicate_symbol(table: &mut SymbolTrackTable, name: &Rc<String>) -> Result<(), String> {
|
let mut seen_identifiers = DuplicateNameTrackTable::new();
|
||||||
match table.entry(name.clone()) {
|
let mut seen_modules = DuplicateNameTrackTable::new();
|
||||||
Entry::Occupied(o) => {
|
|
||||||
let line_number = o.get(); //TODO make this actually work
|
|
||||||
Err(format!("Duplicate definition: {}. It's already defined at {}", name, line_number))
|
|
||||||
},
|
|
||||||
Entry::Vacant(v) => {
|
|
||||||
let line_number = 0; //TODO should work
|
|
||||||
v.insert(line_number);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut seen_identifiers: SymbolTrackTable = HashMap::new();
|
|
||||||
|
|
||||||
for statement in statements.iter() {
|
for statement in statements.iter() {
|
||||||
if let Statement { kind: StatementKind::Declaration(decl), .. } = statement {
|
match statement {
|
||||||
match decl {
|
Statement { kind: StatementKind::Declaration(decl), id } => {
|
||||||
FuncSig(ref signature) => {
|
match decl {
|
||||||
insert_and_check_duplicate_symbol(&mut seen_identifiers, &signature.name)?;
|
FuncSig(ref signature) => {
|
||||||
self.add_function_signature(signature, scope_name_stack)?
|
seen_identifiers.try_register(&signature.name, &id, &self.source_map_handle.borrow())
|
||||||
|
.map_err(|line| format!("Duplicate function definition: {}. It's already defined at {}", signature.name, line))?;
|
||||||
|
self.add_function_signature(signature, scope_name_stack)?
|
||||||
|
}
|
||||||
|
FuncDecl(ref signature, ref body) => {
|
||||||
|
seen_identifiers.try_register(&signature.name, &id, &self.source_map_handle.borrow())
|
||||||
|
.map_err(|line| format!("Duplicate function definition: {}. It's already defined at {}", signature.name, line))?;
|
||||||
|
self.add_function_signature(signature, scope_name_stack)?;
|
||||||
|
scope_name_stack.push(ScopeSegment{
|
||||||
|
name: signature.name.clone(),
|
||||||
|
});
|
||||||
|
let output = self.add_symbols_from_scope(body, scope_name_stack);
|
||||||
|
scope_name_stack.pop();
|
||||||
|
output?
|
||||||
|
},
|
||||||
|
TypeDecl { name, body, mutable } => {
|
||||||
|
seen_identifiers.try_register(&name.name, &id, &self.source_map_handle.borrow())
|
||||||
|
.map_err(|line| format!("Duplicate type definition: {}. It's already defined at {}", name.name, line))?;
|
||||||
|
self.add_type_decl(name, body, mutable, scope_name_stack)?
|
||||||
|
},
|
||||||
|
Binding { name, .. } => {
|
||||||
|
seen_identifiers.try_register(&name, &id, &self.source_map_handle.borrow())
|
||||||
|
.map_err(|line| format!("Duplicate variable definition: {}. It's already defined at {}", name, line))?;
|
||||||
|
self.add_new_symbol(name, scope_name_stack, SymbolSpec::Binding);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
}
|
}
|
||||||
FuncDecl(ref signature, ref body) => {
|
},
|
||||||
insert_and_check_duplicate_symbol(&mut seen_identifiers, &signature.name)?;
|
Statement { kind: StatementKind::Module(ModuleSpecifier { name, contents}), id } => {
|
||||||
self.add_function_signature(signature, scope_name_stack)?;
|
seen_modules.try_register(&name, &id, &self.source_map_handle.borrow())
|
||||||
scope_name_stack.push(ScopeSegment{
|
.map_err(|line| format!("Duplicate module definition: {}. It's already defined at {}", name, line))?;
|
||||||
name: signature.name.clone(),
|
scope_name_stack.push(ScopeSegment { name: name.clone() });
|
||||||
kind: ScopeSegmentKind::Function,
|
let output = self.add_symbols_from_scope(contents, scope_name_stack);
|
||||||
});
|
scope_name_stack.pop();
|
||||||
let output = self.add_symbols_from_scope(body, scope_name_stack);
|
output?
|
||||||
let _ = scope_name_stack.pop();
|
},
|
||||||
output?
|
_ => ()
|
||||||
},
|
|
||||||
TypeDecl { name, body, mutable } => {
|
|
||||||
insert_and_check_duplicate_symbol(&mut seen_identifiers, &name.name)?;
|
|
||||||
//TODO add ScopeSegmentKind::Type here
|
|
||||||
self.add_type_decl(name, body, mutable, scope_name_stack)?
|
|
||||||
},
|
|
||||||
Binding { name, .. } => {
|
|
||||||
insert_and_check_duplicate_symbol(&mut seen_identifiers, name)?;
|
|
||||||
self.add_new_symbol(name, scope_name_stack, SymbolSpec::Binding);
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -243,9 +265,15 @@ impl SymbolTable {
|
|||||||
use crate::ast::{TypeIdentifier, Variant};
|
use crate::ast::{TypeIdentifier, Variant};
|
||||||
let TypeBody(variants) = body;
|
let TypeBody(variants) = body;
|
||||||
let ref type_name = type_name.name;
|
let ref type_name = type_name.name;
|
||||||
|
|
||||||
|
|
||||||
|
let type_spec = SymbolSpec::Type {
|
||||||
|
name: type_name.clone(),
|
||||||
|
};
|
||||||
|
self.add_new_symbol(type_name, &scope_name_stack, type_spec);
|
||||||
|
|
||||||
scope_name_stack.push(ScopeSegment{
|
scope_name_stack.push(ScopeSegment{
|
||||||
name: type_name.clone(),
|
name: type_name.clone(),
|
||||||
kind: ScopeSegmentKind::Type,
|
|
||||||
});
|
});
|
||||||
//TODO figure out why _params isn't being used here
|
//TODO figure out why _params isn't being used here
|
||||||
for (index, var) in variants.iter().enumerate() {
|
for (index, var) in variants.iter().enumerate() {
|
||||||
@@ -313,156 +341,3 @@ impl LocalTypeContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod symbol_table_tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::util::quick_ast;
|
|
||||||
|
|
||||||
macro_rules! values_in_table {
|
|
||||||
//TODO multiple values
|
|
||||||
($source:expr, $single_value:expr) => {
|
|
||||||
{
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast($source);
|
|
||||||
symbol_table.add_top_level_symbols(&ast).unwrap();
|
|
||||||
match symbol_table.lookup_by_fqsn($single_value) {
|
|
||||||
Some(_spec) => (),
|
|
||||||
None => panic!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_symbol_table() {
|
|
||||||
values_in_table! { "let a = 10; fn b() { 20 }", &fqsn!("b"; tr) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_duplicates() {
|
|
||||||
let source = r#"
|
|
||||||
fn a() { 1 }
|
|
||||||
fn b() { 2 }
|
|
||||||
fn a() { 3 }
|
|
||||||
"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
let output = symbol_table.add_top_level_symbols(&ast).unwrap_err();
|
|
||||||
assert!(output.contains("Duplicate"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_duplicates_2() {
|
|
||||||
let source = r#"
|
|
||||||
let a = 20;
|
|
||||||
let q = 39;
|
|
||||||
let a = 30;
|
|
||||||
"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
let output = symbol_table.add_top_level_symbols(&ast).unwrap_err();
|
|
||||||
assert!(output.contains("Duplicate"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_duplicates_3() {
|
|
||||||
let source = r#"
|
|
||||||
fn a() {
|
|
||||||
let a = 20
|
|
||||||
let b = 40
|
|
||||||
a + b
|
|
||||||
}
|
|
||||||
|
|
||||||
fn q() {
|
|
||||||
let x = 30
|
|
||||||
let x = 33
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
let output = symbol_table.add_top_level_symbols(&ast).unwrap_err();
|
|
||||||
assert!(output.contains("Duplicate"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dont_falsely_detect_duplicates() {
|
|
||||||
let source = r#"
|
|
||||||
let a = 20;
|
|
||||||
fn some_func() {
|
|
||||||
let a = 40;
|
|
||||||
77
|
|
||||||
}
|
|
||||||
let q = 39;
|
|
||||||
"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
symbol_table.add_top_level_symbols(&ast).unwrap();
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!["a"; tr]).is_some());
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!["some_func"; fn, "a";tr]).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn enclosing_scopes() {
|
|
||||||
let source = r#"
|
|
||||||
fn outer_func(x) {
|
|
||||||
fn inner_func(arg) {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
x + inner_func(x)
|
|
||||||
}"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
symbol_table.add_top_level_symbols(&ast).unwrap();
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some());
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn enclosing_scopes_2() {
|
|
||||||
let source = r#"
|
|
||||||
fn outer_func(x) {
|
|
||||||
fn inner_func(arg) {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
|
|
||||||
fn second_inner_func() {
|
|
||||||
fn another_inner_func() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner_func(x)
|
|
||||||
}"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
symbol_table.add_top_level_symbols(&ast).unwrap();
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some());
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).is_some());
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; tr)).is_some());
|
|
||||||
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; fn, "another_inner_func"; tr)).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn enclosing_scopes_3() {
|
|
||||||
let source = r#"
|
|
||||||
fn outer_func(x) {
|
|
||||||
fn inner_func(arg) {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
|
|
||||||
fn second_inner_func() {
|
|
||||||
fn another_inner_func() {
|
|
||||||
}
|
|
||||||
fn another_inner_func() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner_func(x)
|
|
||||||
}"#;
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let ast = quick_ast(source);
|
|
||||||
let output = symbol_table.add_top_level_symbols(&ast).unwrap_err();
|
|
||||||
assert!(output.contains("Duplicate"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
51
schala-lang/language/src/symbol_table/symbol_trie.rs
Normal file
51
schala-lang/language/src/symbol_table/symbol_trie.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use radix_trie::{Trie, TrieCommon, TrieKey};
|
||||||
|
use super::FullyQualifiedSymbolName;
|
||||||
|
use std::hash::{Hasher, Hash};
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SymbolTrie(Trie<FullyQualifiedSymbolName, ()>);
|
||||||
|
|
||||||
|
impl TrieKey for FullyQualifiedSymbolName {
|
||||||
|
fn encode_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
let mut output = vec![];
|
||||||
|
let FullyQualifiedSymbolName(scopes) = self;
|
||||||
|
for segment in scopes.iter() {
|
||||||
|
segment.name.as_bytes().hash(&mut hasher);
|
||||||
|
output.extend_from_slice(&hasher.finish().to_be_bytes());
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTrie {
|
||||||
|
pub fn new() -> SymbolTrie {
|
||||||
|
SymbolTrie(Trie::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, fqsn: &FullyQualifiedSymbolName) {
|
||||||
|
self.0.insert(fqsn.clone(), ());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_children(&self, fqsn: &FullyQualifiedSymbolName) -> Vec<FullyQualifiedSymbolName> {
|
||||||
|
let subtrie = match self.0.subtrie(fqsn) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return vec![]
|
||||||
|
};
|
||||||
|
let output: Vec<FullyQualifiedSymbolName> = subtrie.keys().filter(|cur_key| **cur_key != *fqsn).map(|fqsn| fqsn.clone()).collect();
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trie_insertion() {
|
||||||
|
let mut trie = SymbolTrie::new();
|
||||||
|
|
||||||
|
trie.insert(&fqsn!("unrelated"; ty, "thing"; tr));
|
||||||
|
trie.insert(&fqsn!("outer"; ty, "inner"; tr));
|
||||||
|
trie.insert(&fqsn!("outer"; ty, "inner"; ty, "still_inner"; tr));
|
||||||
|
|
||||||
|
let children = trie.get_children(&fqsn!("outer"; ty, "inner"; tr));
|
||||||
|
assert_eq!(children.len(), 1);
|
||||||
|
}
|
||||||
193
schala-lang/language/src/symbol_table/test.rs
Normal file
193
schala-lang/language/src/symbol_table/test.rs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::util::quick_ast;
|
||||||
|
|
||||||
|
fn add_symbols_from_source(src: &str) -> (SymbolTable, Result<(), String>) {
|
||||||
|
let (ast, source_map) = quick_ast(src);
|
||||||
|
let source_map = Rc::new(RefCell::new(source_map));
|
||||||
|
let mut symbol_table = SymbolTable::new(source_map);
|
||||||
|
let result = symbol_table.add_top_level_symbols(&ast);
|
||||||
|
(symbol_table, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! values_in_table {
|
||||||
|
($source:expr, $single_value:expr) => {
|
||||||
|
values_in_table!($source => $single_value);
|
||||||
|
};
|
||||||
|
($source:expr => $( $value:expr ),* ) => {
|
||||||
|
{
|
||||||
|
let (symbol_table, _) = add_symbols_from_source($source);
|
||||||
|
$(
|
||||||
|
match symbol_table.lookup_by_fqsn($value) {
|
||||||
|
Some(_spec) => (),
|
||||||
|
None => panic!(),
|
||||||
|
};
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_symbol_table() {
|
||||||
|
values_in_table! { "let a = 10; fn b() { 20 }", &fqsn!("b"; tr) };
|
||||||
|
values_in_table! { "type Option<T> = Some(T) | None" =>
|
||||||
|
&fqsn!("Option"; tr),
|
||||||
|
&fqsn!("Option"; ty, "Some"; tr),
|
||||||
|
&fqsn!("Option"; ty, "None"; tr) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_function_definition_duplicates() {
|
||||||
|
let source = r#"
|
||||||
|
fn a() { 1 }
|
||||||
|
fn b() { 2 }
|
||||||
|
fn a() { 3 }
|
||||||
|
"#;
|
||||||
|
let (_, output) = add_symbols_from_source(source);
|
||||||
|
assert!(output.unwrap_err().contains("Duplicate function definition: a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_variable_definition_duplicates() {
|
||||||
|
let source = r#"
|
||||||
|
let x = 9
|
||||||
|
let a = 20
|
||||||
|
let q = 39
|
||||||
|
let a = 30
|
||||||
|
"#;
|
||||||
|
let (_, output) = add_symbols_from_source(source);
|
||||||
|
let output = output.unwrap_err();
|
||||||
|
assert!(output.contains("Duplicate variable definition: a"));
|
||||||
|
assert!(output.contains("already defined at 2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_variable_definition_duplicates_in_function() {
|
||||||
|
let source = r#"
|
||||||
|
fn a() {
|
||||||
|
let a = 20
|
||||||
|
let b = 40
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn q() {
|
||||||
|
let a = 29
|
||||||
|
let x = 30
|
||||||
|
let x = 33
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let (_, output) = add_symbols_from_source(source);
|
||||||
|
assert!(output.unwrap_err().contains("Duplicate variable definition: x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dont_falsely_detect_duplicates() {
|
||||||
|
let source = r#"
|
||||||
|
let a = 20;
|
||||||
|
fn some_func() {
|
||||||
|
let a = 40;
|
||||||
|
77
|
||||||
|
}
|
||||||
|
let q = 39;
|
||||||
|
"#;
|
||||||
|
let (symbol_table, _) = add_symbols_from_source(source);
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!["a"; tr]).is_some());
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!["some_func"; fn, "a";tr]).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enclosing_scopes() {
|
||||||
|
let source = r#"
|
||||||
|
fn outer_func(x) {
|
||||||
|
fn inner_func(arg) {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
x + inner_func(x)
|
||||||
|
}"#;
|
||||||
|
let (symbol_table, _) = add_symbols_from_source(source);
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some());
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enclosing_scopes_2() {
|
||||||
|
let source = r#"
|
||||||
|
fn outer_func(x) {
|
||||||
|
fn inner_func(arg) {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn second_inner_func() {
|
||||||
|
fn another_inner_func() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_func(x)
|
||||||
|
}"#;
|
||||||
|
let (symbol_table, _) = add_symbols_from_source(source);
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some());
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).is_some());
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; tr)).is_some());
|
||||||
|
assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; fn, "another_inner_func"; tr)).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enclosing_scopes_3() {
|
||||||
|
let source = r#"
|
||||||
|
fn outer_func(x) {
|
||||||
|
fn inner_func(arg) {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn second_inner_func() {
|
||||||
|
fn another_inner_func() {
|
||||||
|
}
|
||||||
|
fn another_inner_func() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_func(x)
|
||||||
|
}"#;
|
||||||
|
let (_, output) = add_symbols_from_source(source);
|
||||||
|
assert!(output.unwrap_err().contains("Duplicate"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn modules() {
|
||||||
|
let source = r#"
|
||||||
|
module stuff {
|
||||||
|
fn item() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn item()
|
||||||
|
"#;
|
||||||
|
values_in_table! { source =>
|
||||||
|
&fqsn!("item"; tr),
|
||||||
|
&fqsn!("stuff"; tr, "item"; tr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicate_modules() {
|
||||||
|
let source = r#"
|
||||||
|
module q {
|
||||||
|
fn foo() { 4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module a {
|
||||||
|
fn foo() { 334 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module a {
|
||||||
|
fn foo() { 256.1 }
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let (_, output) = add_symbols_from_source(source);
|
||||||
|
let output = output.unwrap_err();
|
||||||
|
assert!(output.contains("Duplicate module"));
|
||||||
|
assert!(output.contains("already defined at 5"));
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ use std::rc::Rc;
|
|||||||
use std::iter::{Iterator, Peekable};
|
use std::iter::{Iterator, Peekable};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::source_map::Location;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum TokenKind {
|
pub enum TokenKind {
|
||||||
Newline, Semicolon,
|
Newline, Semicolon,
|
||||||
@@ -19,7 +21,10 @@ pub enum TokenKind {
|
|||||||
|
|
||||||
Operator(Rc<String>),
|
Operator(Rc<String>),
|
||||||
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
|
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
|
||||||
StrLiteral(Rc<String>),
|
StrLiteral {
|
||||||
|
s: Rc<String>,
|
||||||
|
prefix: Option<Rc<String>>
|
||||||
|
},
|
||||||
Identifier(Rc<String>),
|
Identifier(Rc<String>),
|
||||||
Keyword(Kw),
|
Keyword(Kw),
|
||||||
|
|
||||||
@@ -35,7 +40,7 @@ impl fmt::Display for TokenKind {
|
|||||||
&Operator(ref s) => write!(f, "Operator({})", **s),
|
&Operator(ref s) => write!(f, "Operator({})", **s),
|
||||||
&DigitGroup(ref s) => write!(f, "DigitGroup({})", s),
|
&DigitGroup(ref s) => write!(f, "DigitGroup({})", s),
|
||||||
&HexLiteral(ref s) => write!(f, "HexLiteral({})", s),
|
&HexLiteral(ref s) => write!(f, "HexLiteral({})", s),
|
||||||
&StrLiteral(ref s) => write!(f, "StrLiteral({})", s),
|
&StrLiteral {ref s, .. } => write!(f, "StrLiteral({})", s),
|
||||||
&Identifier(ref s) => write!(f, "Identifier({})", s),
|
&Identifier(ref s) => write!(f, "Identifier({})", s),
|
||||||
&Error(ref s) => write!(f, "Error({})", s),
|
&Error(ref s) => write!(f, "Error({})", s),
|
||||||
other => write!(f, "{:?}", other),
|
other => write!(f, "{:?}", other),
|
||||||
@@ -86,11 +91,10 @@ lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
pub kind: TokenKind,
|
pub kind: TokenKind,
|
||||||
pub line_num: usize,
|
pub location: Location,
|
||||||
pub char_num: usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
@@ -101,7 +105,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn to_string_with_metadata(&self) -> String {
|
pub fn to_string_with_metadata(&self) -> String {
|
||||||
format!("{}(L:{},c:{})", self.kind, self.line_num, self.char_num)
|
format!("{}({})", self.kind, self.location)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_kind(&self) -> TokenKind {
|
pub fn get_kind(&self) -> TokenKind {
|
||||||
@@ -162,14 +166,15 @@ pub fn tokenize(input: &str) -> Vec<Token> {
|
|||||||
'(' => LParen, ')' => RParen,
|
'(' => LParen, ')' => RParen,
|
||||||
'{' => LCurlyBrace, '}' => RCurlyBrace,
|
'{' => LCurlyBrace, '}' => RCurlyBrace,
|
||||||
'[' => LSquareBracket, ']' => RSquareBracket,
|
'[' => LSquareBracket, ']' => RSquareBracket,
|
||||||
'"' => handle_quote(&mut input),
|
'"' => handle_quote(&mut input, None),
|
||||||
'\\' => Backslash,
|
'\\' => Backslash,
|
||||||
c if c.is_digit(10) => handle_digit(c, &mut input),
|
c if c.is_digit(10) => handle_digit(c, &mut input),
|
||||||
c if c.is_alphabetic() || c == '_' => handle_alphabetic(c, &mut input),
|
c if c.is_alphabetic() || c == '_' => handle_alphabetic(c, &mut input),
|
||||||
c if is_operator(&c) => handle_operator(c, &mut input),
|
c if is_operator(&c) => handle_operator(c, &mut input),
|
||||||
unknown => Error(format!("Unexpected character: {}", unknown)),
|
unknown => Error(format!("Unexpected character: {}", unknown)),
|
||||||
};
|
};
|
||||||
tokens.push(Token { kind: cur_tok_kind, line_num, char_num });
|
let location = Location { line_num, char_num };
|
||||||
|
tokens.push(Token { kind: cur_tok_kind, location });
|
||||||
}
|
}
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
@@ -189,7 +194,7 @@ fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
|
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>, quote_prefix: Option<&str>) -> TokenKind {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
loop {
|
loop {
|
||||||
match input.next().map(|(_, _, c)| { c }) {
|
match input.next().map(|(_, _, c)| { c }) {
|
||||||
@@ -211,7 +216,7 @@ fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind
|
|||||||
None => return TokenKind::Error(format!("Unclosed string")),
|
None => return TokenKind::Error(format!("Unclosed string")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenKind::StrLiteral(Rc::new(buf))
|
TokenKind::StrLiteral { s: Rc::new(buf), prefix: quote_prefix.map(|s| Rc::new(s.to_string())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
|
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
|
||||||
@@ -223,6 +228,10 @@ fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match input.peek().map(|&(_, _, c)| { c }) {
|
match input.peek().map(|&(_, _, c)| { c }) {
|
||||||
|
Some(c) if c == '"' => {
|
||||||
|
input.next();
|
||||||
|
return handle_quote(input, Some(&buf));
|
||||||
|
},
|
||||||
Some(c) if c.is_alphanumeric() || c == '_' => {
|
Some(c) if c.is_alphanumeric() || c == '_' => {
|
||||||
input.next();
|
input.next();
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
@@ -323,4 +332,13 @@ mod schala_tokenizer_tests {
|
|||||||
let token_kinds: Vec<TokenKind> = tokenize("1 `plus` 2").into_iter().map(move |t| t.kind).collect();
|
let token_kinds: Vec<TokenKind> = tokenize("1 `plus` 2").into_iter().map(move |t| t.kind).collect();
|
||||||
assert_eq!(token_kinds, vec![digit!("1"), op!("plus"), digit!("2")]);
|
assert_eq!(token_kinds, vec![digit!("1"), op!("plus"), digit!("2")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_literals() {
|
||||||
|
let token_kinds: Vec<TokenKind> = tokenize(r#""some string""#).into_iter().map(move |t| t.kind).collect();
|
||||||
|
assert_eq!(token_kinds, vec![StrLiteral { s: Rc::new("some string".to_string()), prefix: None }]);
|
||||||
|
|
||||||
|
let token_kinds: Vec<TokenKind> = tokenize(r#"b"some bytestring""#).into_iter().map(move |t| t.kind).collect();
|
||||||
|
assert_eq!(token_kinds, vec![StrLiteral { s: Rc::new("some bytestring".to_string()), prefix: Some(Rc::new("b".to_string())) }]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub struct TypeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TypeData {
|
impl TypeData {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn new() -> TypeData {
|
pub fn new() -> TypeData {
|
||||||
TypeData { ty: None }
|
TypeData { ty: None }
|
||||||
}
|
}
|
||||||
@@ -276,6 +277,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
StatementKind::Expression(e) => self.expr(e),
|
StatementKind::Expression(e) => self.expr(e),
|
||||||
StatementKind::Declaration(decl) => self.decl(&decl),
|
StatementKind::Declaration(decl) => self.decl(&decl),
|
||||||
StatementKind::Import(_) => Ok(ty!(Unit)),
|
StatementKind::Import(_) => Ok(ty!(Unit)),
|
||||||
|
StatementKind::Module(_) => Ok(ty!(Unit)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +462,7 @@ mod typechecking_tests {
|
|||||||
macro_rules! assert_type_in_fresh_context {
|
macro_rules! assert_type_in_fresh_context {
|
||||||
($string:expr, $type:expr) => {
|
($string:expr, $type:expr) => {
|
||||||
let mut tc = TypeContext::new();
|
let mut tc = TypeContext::new();
|
||||||
let ref ast = crate::util::quick_ast($string);
|
let (ref ast, _) = crate::util::quick_ast($string);
|
||||||
let ty = tc.typecheck(ast).unwrap();
|
let ty = tc.typecheck(ast).unwrap();
|
||||||
assert_eq!(ty, $type)
|
assert_eq!(ty, $type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,18 @@ impl<'a, T, V> ScopeStack<'a, T, V> where T: Hash + Eq {
|
|||||||
|
|
||||||
/// this is intended for use in tests, and does no error-handling whatsoever
|
/// this is intended for use in tests, and does no error-handling whatsoever
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn quick_ast(input: &str) -> crate::ast::AST {
|
pub fn quick_ast(input: &str) -> (crate::ast::AST, crate::source_map::SourceMap) {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
let source_map = crate::source_map::SourceMap::new();
|
||||||
|
let source_map_handle = Rc::new(RefCell::new(source_map));
|
||||||
let tokens = crate::tokenizing::tokenize(input);
|
let tokens = crate::tokenizing::tokenize(input);
|
||||||
let mut parser = crate::parsing::Parser::new(tokens);
|
let mut parser = crate::parsing::Parser::new(source_map_handle.clone());
|
||||||
parser.parse().unwrap()
|
parser.add_new_tokens(tokens);
|
||||||
|
let output = parser.parse();
|
||||||
|
std::mem::drop(parser);
|
||||||
|
(output.unwrap(), Rc::try_unwrap(source_map_handle).map_err(|_| ()).unwrap().into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ impl CommandTree {
|
|||||||
}
|
}
|
||||||
pub fn get_help(&self) -> &str {
|
pub fn get_help(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
CommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
|
CommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
|
||||||
CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
|
CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
|
||||||
CommandTree::Top(_) => ""
|
CommandTree::Top(_) => ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,12 @@ pub fn help(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
|
|||||||
None => format!("Directive `{}` not found", commands.last().unwrap()),
|
None => format!("Directive `{}` not found", commands.last().unwrap()),
|
||||||
Some(dir) => {
|
Some(dir) => {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
writeln!(buf, "`{}` - {}", dir.get_cmd(), dir.get_help()).unwrap();
|
let cmd = dir.get_cmd();
|
||||||
|
let children = dir.get_children();
|
||||||
|
writeln!(buf, "`{}` - {}", cmd, dir.get_help()).unwrap();
|
||||||
|
for sub in children.iter() {
|
||||||
|
writeln!(buf, "\t`{} {}` - {}", cmd, sub.get_cmd(), sub.get_help()).unwrap();
|
||||||
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user