mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
commit
e6e61251ab
26 changed files with 284 additions and 260 deletions
15
.travis.yml
15
.travis.yml
|
@ -1,3 +1,14 @@
|
||||||
language: rust
|
language: rust
|
||||||
rust:
|
|
||||||
- stable
|
matrix:
|
||||||
|
include:
|
||||||
|
|
||||||
|
- rust: nightly-2018-01-26
|
||||||
|
before_script:
|
||||||
|
- rustup component add rustfmt-preview
|
||||||
|
script:
|
||||||
|
- cargo fmt -- --write-mode=diff
|
||||||
|
|
||||||
|
- rust: stable
|
||||||
|
script:
|
||||||
|
- cargo test
|
||||||
|
|
0
rustfmt.toml
Normal file
0
rustfmt.toml
Normal file
|
@ -2,8 +2,8 @@ extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
extern crate ron;
|
|
||||||
extern crate file;
|
extern crate file;
|
||||||
|
extern crate ron;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -33,8 +33,9 @@ impl Grammar {
|
||||||
acc.push_str("use tree::{SyntaxKind, SyntaxInfo};\n");
|
acc.push_str("use tree::{SyntaxKind, SyntaxInfo};\n");
|
||||||
acc.push_str("\n");
|
acc.push_str("\n");
|
||||||
|
|
||||||
let syntax_kinds: Vec<String> =
|
let syntax_kinds: Vec<String> = self.keywords
|
||||||
self.keywords.iter().map(|kw| kw_token(kw))
|
.iter()
|
||||||
|
.map(|kw| kw_token(kw))
|
||||||
.chain(self.tokens.iter().cloned())
|
.chain(self.tokens.iter().cloned())
|
||||||
.chain(self.nodes.iter().cloned())
|
.chain(self.nodes.iter().cloned())
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -48,7 +49,11 @@ impl Grammar {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
acc.push_str("\n");
|
acc.push_str("\n");
|
||||||
write!(acc, "static INFOS: [SyntaxInfo; {}] = [\n", syntax_kinds.len()).unwrap();
|
write!(
|
||||||
|
acc,
|
||||||
|
"static INFOS: [SyntaxInfo; {}] = [\n",
|
||||||
|
syntax_kinds.len()
|
||||||
|
).unwrap();
|
||||||
for kind in syntax_kinds.iter() {
|
for kind in syntax_kinds.iter() {
|
||||||
let sname = scream(kind);
|
let sname = scream(kind);
|
||||||
write!(
|
write!(
|
||||||
|
|
|
@ -2,7 +2,7 @@ extern crate libsyntax2;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use libsyntax2::{tokenize, parse};
|
use libsyntax2::{parse, tokenize};
|
||||||
use libsyntax2::utils::dump_tree;
|
use libsyntax2::utils::dump_tree;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
pub fn is_ident_start(c: char) -> bool {
|
pub fn is_ident_start(c: char) -> bool {
|
||||||
(c >= 'a' && c <= 'z')
|
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
|
||||||
|| (c >= 'A' && c <= 'Z')
|
|
||||||
|| c == '_'
|
|
||||||
|| (c > '\x7f' && UnicodeXID::is_xid_start(c))
|
|| (c > '\x7f' && UnicodeXID::is_xid_start(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ident_continue(c: char) -> bool {
|
pub fn is_ident_continue(c: char) -> bool {
|
||||||
(c >= 'a' && c <= 'z')
|
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'
|
||||||
|| (c >= 'A' && c <= 'Z')
|
|
||||||
|| (c >= '0' && c <= '9')
|
|
||||||
|| c == '_'
|
|
||||||
|| (c > '\x7f' && UnicodeXID::is_xid_continue(c))
|
|| (c > '\x7f' && UnicodeXID::is_xid_continue(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use lexer::ptr::Ptr;
|
use lexer::ptr::Ptr;
|
||||||
|
|
||||||
use {SyntaxKind};
|
use SyntaxKind;
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
|
|
||||||
pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool {
|
pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool {
|
||||||
|
@ -23,7 +23,6 @@ pub(crate) fn scan_comment(ptr: &mut Ptr) -> Option<SyntaxKind> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn bump_until_eol(ptr: &mut Ptr) {
|
fn bump_until_eol(ptr: &mut Ptr) {
|
||||||
loop {
|
loop {
|
||||||
if ptr.next_is('\n') || ptr.next_is('\r') && ptr.nnext_is('\n') {
|
if ptr.next_is('\n') || ptr.next_is('\r') && ptr.nnext_is('\n') {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use {Token, SyntaxKind};
|
use {SyntaxKind, Token};
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
|
|
||||||
mod ptr;
|
mod ptr;
|
||||||
|
@ -11,10 +11,11 @@ mod numbers;
|
||||||
use self::numbers::scan_number;
|
use self::numbers::scan_number;
|
||||||
|
|
||||||
mod strings;
|
mod strings;
|
||||||
use self::strings::{is_string_literal_start, scan_char, scan_byte_char_or_string, scan_string, scan_raw_string};
|
use self::strings::{is_string_literal_start, scan_byte_char_or_string, scan_char, scan_raw_string,
|
||||||
|
scan_string};
|
||||||
|
|
||||||
mod comments;
|
mod comments;
|
||||||
use self::comments::{scan_shebang, scan_comment};
|
use self::comments::{scan_comment, scan_shebang};
|
||||||
|
|
||||||
pub fn tokenize(text: &str) -> Vec<Token> {
|
pub fn tokenize(text: &str) -> Vec<Token> {
|
||||||
let mut text = text;
|
let mut text = text;
|
||||||
|
@ -45,10 +46,10 @@ fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
||||||
match c {
|
match c {
|
||||||
'#' => if scan_shebang(ptr) {
|
'#' => if scan_shebang(ptr) {
|
||||||
return SHEBANG;
|
return SHEBANG;
|
||||||
}
|
},
|
||||||
'/' => if let Some(kind) = scan_comment(ptr) {
|
'/' => if let Some(kind) = scan_comment(ptr) {
|
||||||
return kind;
|
return kind;
|
||||||
}
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,31 +90,36 @@ fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
||||||
'%' => return PERCENT,
|
'%' => return PERCENT,
|
||||||
|
|
||||||
// Multi-byte tokens.
|
// Multi-byte tokens.
|
||||||
'.' => return match (ptr.next(), ptr.nnext()) {
|
'.' => {
|
||||||
|
return match (ptr.next(), ptr.nnext()) {
|
||||||
(Some('.'), Some('.')) => {
|
(Some('.'), Some('.')) => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
DOTDOTDOT
|
DOTDOTDOT
|
||||||
},
|
}
|
||||||
(Some('.'), Some('=')) => {
|
(Some('.'), Some('=')) => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
DOTDOTEQ
|
DOTDOTEQ
|
||||||
},
|
}
|
||||||
(Some('.'), _) => {
|
(Some('.'), _) => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
DOTDOT
|
DOTDOT
|
||||||
},
|
}
|
||||||
_ => DOT
|
_ => DOT,
|
||||||
},
|
}
|
||||||
':' => return match ptr.next() {
|
}
|
||||||
|
':' => {
|
||||||
|
return match ptr.next() {
|
||||||
Some(':') => {
|
Some(':') => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
COLONCOLON
|
COLONCOLON
|
||||||
}
|
}
|
||||||
_ => COLON
|
_ => COLON,
|
||||||
},
|
}
|
||||||
'=' => return match ptr.next() {
|
}
|
||||||
|
'=' => {
|
||||||
|
return match ptr.next() {
|
||||||
Some('=') => {
|
Some('=') => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
EQEQ
|
EQEQ
|
||||||
|
@ -123,24 +129,30 @@ fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
||||||
FAT_ARROW
|
FAT_ARROW
|
||||||
}
|
}
|
||||||
_ => EQ,
|
_ => EQ,
|
||||||
},
|
}
|
||||||
'!' => return match ptr.next() {
|
}
|
||||||
|
'!' => {
|
||||||
|
return match ptr.next() {
|
||||||
Some('=') => {
|
Some('=') => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
NEQ
|
NEQ
|
||||||
}
|
}
|
||||||
_ => EXCL,
|
_ => EXCL,
|
||||||
},
|
}
|
||||||
'-' => return if ptr.next_is('>') {
|
}
|
||||||
|
'-' => {
|
||||||
|
return if ptr.next_is('>') {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
THIN_ARROW
|
THIN_ARROW
|
||||||
} else {
|
} else {
|
||||||
MINUS
|
MINUS
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the character is an ident start not followed by another single
|
// If the character is an ident start not followed by another single
|
||||||
// quote, then this is a lifetime name:
|
// quote, then this is a lifetime name:
|
||||||
'\'' => return if ptr.next_is_p(is_ident_start) && !ptr.nnext_is('\'') {
|
'\'' => {
|
||||||
|
return if ptr.next_is_p(is_ident_start) && !ptr.nnext_is('\'') {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
while ptr.next_is_p(is_ident_continue) {
|
while ptr.next_is_p(is_ident_continue) {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
|
@ -156,12 +168,13 @@ fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
||||||
scan_char(ptr);
|
scan_char(ptr);
|
||||||
scan_literal_suffix(ptr);
|
scan_literal_suffix(ptr);
|
||||||
CHAR
|
CHAR
|
||||||
},
|
};
|
||||||
|
}
|
||||||
'b' => {
|
'b' => {
|
||||||
let kind = scan_byte_char_or_string(ptr);
|
let kind = scan_byte_char_or_string(ptr);
|
||||||
scan_literal_suffix(ptr);
|
scan_literal_suffix(ptr);
|
||||||
return kind
|
return kind;
|
||||||
},
|
}
|
||||||
'"' => {
|
'"' => {
|
||||||
scan_string(ptr);
|
scan_string(ptr);
|
||||||
scan_literal_suffix(ptr);
|
scan_literal_suffix(ptr);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use lexer::ptr::Ptr;
|
use lexer::ptr::Ptr;
|
||||||
use lexer::classes::*;
|
use lexer::classes::*;
|
||||||
|
|
||||||
use {SyntaxKind};
|
use SyntaxKind;
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
|
|
||||||
pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind {
|
||||||
|
@ -52,7 +52,7 @@ fn scan_digits(ptr: &mut Ptr, allow_hex: bool) {
|
||||||
'a'...'f' | 'A'...'F' if allow_hex => {
|
'a'...'f' | 'A'...'F' if allow_hex => {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
}
|
}
|
||||||
_ => return
|
_ => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use {TextUnit};
|
use TextUnit;
|
||||||
|
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
|
||||||
|
@ -9,7 +9,10 @@ pub(crate) struct Ptr<'s> {
|
||||||
|
|
||||||
impl<'s> Ptr<'s> {
|
impl<'s> Ptr<'s> {
|
||||||
pub fn new(text: &'s str) -> Ptr<'s> {
|
pub fn new(text: &'s str) -> Ptr<'s> {
|
||||||
Ptr { text, len: TextUnit::new(0) }
|
Ptr {
|
||||||
|
text,
|
||||||
|
len: TextUnit::new(0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_len(self) -> TextUnit {
|
pub fn into_len(self) -> TextUnit {
|
||||||
|
@ -53,7 +56,7 @@ impl<'s> Ptr<'s> {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
Some(c) if pred(c) => {
|
Some(c) if pred(c) => {
|
||||||
self.bump();
|
self.bump();
|
||||||
},
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use {SyntaxKind};
|
use SyntaxKind;
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
|
|
||||||
use lexer::ptr::Ptr;
|
use lexer::ptr::Ptr;
|
||||||
|
|
||||||
pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool {
|
pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool {
|
||||||
match (c, c1, c2) {
|
match (c, c1, c2) {
|
||||||
('r', Some('"'), _) |
|
('r', Some('"'), _)
|
||||||
('r', Some('#'), _) |
|
| ('r', Some('#'), _)
|
||||||
('b', Some('"'), _) |
|
| ('b', Some('"'), _)
|
||||||
('b', Some('\''), _) |
|
| ('b', Some('\''), _)
|
||||||
('b', Some('r'), Some('"')) |
|
| ('b', Some('r'), Some('"'))
|
||||||
('b', Some('r'), Some('#')) => true,
|
| ('b', Some('r'), Some('#')) => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,20 +50,20 @@ pub(crate) fn scan_byte_char_or_string(ptr: &mut Ptr) -> SyntaxKind {
|
||||||
pub(crate) fn scan_string(ptr: &mut Ptr) {
|
pub(crate) fn scan_string(ptr: &mut Ptr) {
|
||||||
while let Some(c) = ptr.bump() {
|
while let Some(c) = ptr.bump() {
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn scan_raw_string(ptr: &mut Ptr) {
|
pub(crate) fn scan_raw_string(ptr: &mut Ptr) {
|
||||||
if !ptr.next_is('"') {
|
if !ptr.next_is('"') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
|
|
||||||
while let Some(c) = ptr.bump() {
|
while let Some(c) = ptr.bump() {
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,32 +71,32 @@ pub(crate) fn scan_raw_string(ptr: &mut Ptr) {
|
||||||
fn scan_byte(ptr: &mut Ptr) {
|
fn scan_byte(ptr: &mut Ptr) {
|
||||||
if ptr.next_is('\'') {
|
if ptr.next_is('\'') {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
if ptr.next_is('\'') {
|
if ptr.next_is('\'') {
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_byte_string(ptr: &mut Ptr) {
|
fn scan_byte_string(ptr: &mut Ptr) {
|
||||||
while let Some(c) = ptr.bump() {
|
while let Some(c) = ptr.bump() {
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_raw_byte_string(ptr: &mut Ptr) {
|
fn scan_raw_byte_string(ptr: &mut Ptr) {
|
||||||
if !ptr.next_is('"') {
|
if !ptr.next_is('"') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
|
|
||||||
while let Some(c) = ptr.bump() {
|
while let Some(c) = ptr.bump() {
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,4 +105,3 @@ fn scan_char_or_byte(ptr: &mut Ptr) {
|
||||||
//FIXME: deal with escape sequencies
|
//FIXME: deal with escape sequencies
|
||||||
ptr.bump();
|
ptr.bump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ mod tree;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
pub mod syntax_kinds;
|
pub mod syntax_kinds;
|
||||||
pub use text::{TextUnit, TextRange};
|
pub use text::{TextRange, TextUnit};
|
||||||
pub use tree::{SyntaxKind, Token, FileBuilder, Sink, File, Node};
|
pub use tree::{File, FileBuilder, Node, Sink, SyntaxKind, Token};
|
||||||
pub use lexer::{next_token, tokenize};
|
pub use lexer::{next_token, tokenize};
|
||||||
pub use parser::parse;
|
pub use parser::parse;
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ pub mod utils {
|
||||||
buff.push_str(&String::from(" ").repeat(level));
|
buff.push_str(&String::from(" ").repeat(level));
|
||||||
write!(buff, "{:?}\n", node).unwrap();
|
write!(buff, "{:?}\n", node).unwrap();
|
||||||
let my_errors = node.errors().filter(|e| e.after_child().is_none());
|
let my_errors = node.errors().filter(|e| e.after_child().is_none());
|
||||||
let parent_errors = node.parent().into_iter()
|
let parent_errors = node.parent()
|
||||||
|
.into_iter()
|
||||||
.flat_map(|n| n.errors())
|
.flat_map(|n| n.errors())
|
||||||
.filter(|e| e.after_child() == Some(node));
|
.filter(|e| e.after_child() == Some(node));
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ pub(super) fn outer_attributes(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn attribute(p: &mut Parser, inner: bool) {
|
fn attribute(p: &mut Parser, inner: bool) {
|
||||||
let attr = p.start();
|
let attr = p.start();
|
||||||
assert!(p.at(POUND));
|
assert!(p.at(POUND));
|
||||||
|
@ -38,9 +37,7 @@ fn meta_item(p: &mut Parser) {
|
||||||
EQ => {
|
EQ => {
|
||||||
p.bump();
|
p.bump();
|
||||||
if !expressions::literal(p) {
|
if !expressions::literal(p) {
|
||||||
p.error()
|
p.error().message("expected literal").emit();
|
||||||
.message("expected literal")
|
|
||||||
.emit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
L_PAREN => meta_item_arg_list(p),
|
L_PAREN => meta_item_arg_list(p),
|
||||||
|
@ -48,9 +45,7 @@ fn meta_item(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
meta_item.complete(p, META_ITEM);
|
meta_item.complete(p, META_ITEM);
|
||||||
} else {
|
} else {
|
||||||
p.error()
|
p.error().message("expected attribute value").emit()
|
||||||
.message("expected attribute value")
|
|
||||||
.emit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +68,8 @@ fn meta_item_arg_list(p: &mut Parser) {
|
||||||
p.error().message(message).emit();
|
p.error().message(message).emit();
|
||||||
p.bump();
|
p.bump();
|
||||||
err.complete(p, ERROR);
|
err.complete(p, ERROR);
|
||||||
continue
|
continue;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
if !p.at(R_PAREN) {
|
if !p.at(R_PAREN) {
|
||||||
p.expect(COMMA);
|
p.expect(COMMA);
|
||||||
|
|
|
@ -2,15 +2,13 @@ use super::*;
|
||||||
|
|
||||||
pub(super) fn literal(p: &mut Parser) -> bool {
|
pub(super) fn literal(p: &mut Parser) -> bool {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
TRUE_KW | FALSE_KW |
|
TRUE_KW | FALSE_KW | INT_NUMBER | FLOAT_NUMBER | BYTE | CHAR | STRING | RAW_STRING
|
||||||
INT_NUMBER | FLOAT_NUMBER |
|
| BYTE_STRING | RAW_BYTE_STRING => {
|
||||||
BYTE | CHAR |
|
|
||||||
STRING | RAW_STRING | BYTE_STRING | RAW_BYTE_STRING => {
|
|
||||||
let lit = p.start();
|
let lit = p.start();
|
||||||
p.bump();
|
p.bump();
|
||||||
lit.complete(p, LITERAL);
|
lit.complete(p, LITERAL);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,8 @@ pub(super) fn mod_contents(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) const ITEM_FIRST: TokenSet = token_set![
|
pub(super) const ITEM_FIRST: TokenSet =
|
||||||
EXTERN_KW,
|
token_set![EXTERN_KW, MOD_KW, USE_KW, STRUCT_KW, FN_KW, PUB_KW, POUND,];
|
||||||
MOD_KW,
|
|
||||||
USE_KW,
|
|
||||||
STRUCT_KW,
|
|
||||||
FN_KW,
|
|
||||||
PUB_KW,
|
|
||||||
POUND,
|
|
||||||
];
|
|
||||||
|
|
||||||
fn item(p: &mut Parser) {
|
fn item(p: &mut Parser) {
|
||||||
let item = p.start();
|
let item = p.start();
|
||||||
|
@ -76,10 +69,9 @@ fn struct_item(p: &mut Parser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
L_CURLY => named_fields(p),
|
L_CURLY => named_fields(p),
|
||||||
_ => { //TODO: special case `(` error message
|
_ => {
|
||||||
p.error()
|
//TODO: special case `(` error message
|
||||||
.message("expected `;` or `{`")
|
p.error().message("expected `;` or `{`").emit();
|
||||||
.emit();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,9 +86,7 @@ fn struct_item(p: &mut Parser) {
|
||||||
p.expect(SEMI);
|
p.expect(SEMI);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
p.error()
|
p.error().message("expected `;`, `{`, or `(`").emit();
|
||||||
.message("expected `;`, `{`, or `(`")
|
|
||||||
.emit();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,9 +199,7 @@ fn use_item(p: &mut Parser) {
|
||||||
L_CURLY => nested_trees(p),
|
L_CURLY => nested_trees(p),
|
||||||
_ => {
|
_ => {
|
||||||
// is this unreachable?
|
// is this unreachable?
|
||||||
p.error()
|
p.error().message("expected `{` or `*`").emit();
|
||||||
.message("expected `{` or `*`")
|
|
||||||
.emit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +210,7 @@ fn use_item(p: &mut Parser) {
|
||||||
m.abandon(p);
|
m.abandon(p);
|
||||||
p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super`, `indent`");
|
p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super`, `indent`");
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
m.complete(p, USE_TREE);
|
m.complete(p, USE_TREE);
|
||||||
}
|
}
|
||||||
|
@ -240,13 +228,9 @@ fn use_item(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn fn_item(p: &mut Parser) {
|
fn fn_item(p: &mut Parser) {
|
||||||
assert!(p.at(FN_KW));
|
assert!(p.at(FN_KW));
|
||||||
p.bump();
|
p.bump();
|
||||||
|
|
||||||
p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN)
|
p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN) && p.curly_block(|_| ());
|
||||||
&& p.curly_block(|_| ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::parser::{Parser, TokenSet};
|
use super::parser::{Parser, TokenSet};
|
||||||
use {SyntaxKind};
|
use SyntaxKind;
|
||||||
use tree::EOF;
|
use tree::EOF;
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ fn visibility(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
p.expect(R_PAREN);
|
p.expect(R_PAREN);
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vis.complete(p, VISIBILITY);
|
vis.complete(p, VISIBILITY);
|
||||||
|
@ -53,9 +53,7 @@ impl<'p> Parser<'p> {
|
||||||
|
|
||||||
fn err_and_bump(&mut self, message: &str) {
|
fn err_and_bump(&mut self, message: &str) {
|
||||||
let err = self.start();
|
let err = self.start();
|
||||||
self.error()
|
self.error().message(message).emit();
|
||||||
.message(message)
|
|
||||||
.emit();
|
|
||||||
self.bump();
|
self.bump();
|
||||||
err.complete(self, ERROR);
|
err.complete(self, ERROR);
|
||||||
}
|
}
|
||||||
|
@ -65,15 +63,16 @@ impl<'p> Parser<'p> {
|
||||||
self.bump();
|
self.bump();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.error()
|
self.error().message(format!("expected {:?}", kind)).emit();
|
||||||
.message(format!("expected {:?}", kind))
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat(&mut self, kind: SyntaxKind) -> bool {
|
fn eat(&mut self, kind: SyntaxKind) -> bool {
|
||||||
self.current() == kind && { self.bump(); true }
|
self.current() == kind && {
|
||||||
|
self.bump();
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,8 +93,7 @@ impl Lookahead for SyntaxKind {
|
||||||
|
|
||||||
impl Lookahead for [SyntaxKind; 2] {
|
impl Lookahead for [SyntaxKind; 2] {
|
||||||
fn is_ahead(self, p: &Parser) -> bool {
|
fn is_ahead(self, p: &Parser) -> bool {
|
||||||
p.current() == self[0]
|
p.current() == self[0] && p.raw_lookahead(1) == self[1]
|
||||||
&& p.raw_lookahead(1) == self[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(p: &mut Parser) {
|
fn consume(p: &mut Parser) {
|
||||||
|
@ -106,9 +104,7 @@ impl Lookahead for [SyntaxKind; 2] {
|
||||||
|
|
||||||
impl Lookahead for [SyntaxKind; 3] {
|
impl Lookahead for [SyntaxKind; 3] {
|
||||||
fn is_ahead(self, p: &Parser) -> bool {
|
fn is_ahead(self, p: &Parser) -> bool {
|
||||||
p.current() == self[0]
|
p.current() == self[0] && p.raw_lookahead(1) == self[1] && p.raw_lookahead(2) == self[2]
|
||||||
&& p.raw_lookahead(1) == self[1]
|
|
||||||
&& p.raw_lookahead(2) == self[2]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(p: &mut Parser) {
|
fn consume(p: &mut Parser) {
|
||||||
|
@ -130,5 +126,4 @@ impl<'a> Lookahead for AnyOf<'a> {
|
||||||
fn consume(p: &mut Parser) {
|
fn consume(p: &mut Parser) {
|
||||||
p.bump();
|
p.bump();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,7 @@ fn path_segment(p: &mut Parser, first: bool) {
|
||||||
p.bump();
|
p.bump();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
p.error()
|
p.error().message("expected identifier").emit();
|
||||||
.message("expected identifier")
|
|
||||||
.emit();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
segment.complete(p, PATH_SEGMENT);
|
segment.complete(p, PATH_SEGMENT);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use {Token, SyntaxKind};
|
use {SyntaxKind, Token};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
use {Token, SyntaxKind, TextUnit};
|
use {SyntaxKind, TextUnit, Token};
|
||||||
use super::Event;
|
use super::Event;
|
||||||
use super::super::is_insignificant;
|
use super::super::is_insignificant;
|
||||||
use syntax_kinds::{L_CURLY, R_CURLY, ERROR};
|
use syntax_kinds::{ERROR, L_CURLY, R_CURLY};
|
||||||
use tree::{EOF, TOMBSTONE};
|
use tree::{EOF, TOMBSTONE};
|
||||||
|
|
||||||
pub(crate) struct Marker {
|
pub(crate) struct Marker {
|
||||||
pos: u32
|
pos: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marker {
|
impl Marker {
|
||||||
pub fn complete(self, p: &mut Parser, kind: SyntaxKind) -> CompleteMarker {
|
pub fn complete(self, p: &mut Parser, kind: SyntaxKind) -> CompleteMarker {
|
||||||
match self.event(p) {
|
match self.event(p) {
|
||||||
&mut Event::Start { kind: ref mut slot, ..} => {
|
&mut Event::Start {
|
||||||
|
kind: ref mut slot, ..
|
||||||
|
} => {
|
||||||
*slot = kind;
|
*slot = kind;
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -26,8 +28,11 @@ impl Marker {
|
||||||
let idx = self.pos as usize;
|
let idx = self.pos as usize;
|
||||||
if idx == p.events.len() - 1 {
|
if idx == p.events.len() - 1 {
|
||||||
match p.events.pop() {
|
match p.events.pop() {
|
||||||
Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
|
Some(Event::Start {
|
||||||
_ => unreachable!()
|
kind: TOMBSTONE,
|
||||||
|
forward_parent: None,
|
||||||
|
}) => (),
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::std::mem::forget(self);
|
::std::mem::forget(self);
|
||||||
|
@ -51,14 +56,17 @@ impl Drop for Marker {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct CompleteMarker {
|
pub(crate) struct CompleteMarker {
|
||||||
pos: u32
|
pos: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompleteMarker {
|
impl CompleteMarker {
|
||||||
pub(crate) fn precede(self, p: &mut Parser) -> Marker {
|
pub(crate) fn precede(self, p: &mut Parser) -> Marker {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
match p.events[self.pos as usize] {
|
match p.events[self.pos as usize] {
|
||||||
Event::Start { ref mut forward_parent, ..} => {
|
Event::Start {
|
||||||
|
ref mut forward_parent,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
*forward_parent = Some(m.pos - self.pos);
|
*forward_parent = Some(m.pos - self.pos);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -68,7 +76,7 @@ impl CompleteMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TokenSet {
|
pub(crate) struct TokenSet {
|
||||||
pub tokens: &'static [SyntaxKind]
|
pub tokens: &'static [SyntaxKind],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenSet {
|
impl TokenSet {
|
||||||
|
@ -90,7 +98,6 @@ macro_rules! token_set {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) struct Parser<'t> {
|
pub(crate) struct Parser<'t> {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
text: &'t str,
|
text: &'t str,
|
||||||
|
@ -150,8 +157,13 @@ impl<'t> Parser<'t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start(&mut self) -> Marker {
|
pub(crate) fn start(&mut self) -> Marker {
|
||||||
let m = Marker { pos: self.events.len() as u32 };
|
let m = Marker {
|
||||||
self.event(Event::Start { kind: TOMBSTONE, forward_parent: None });
|
pos: self.events.len() as u32,
|
||||||
|
};
|
||||||
|
self.event(Event::Start {
|
||||||
|
kind: TOMBSTONE,
|
||||||
|
forward_parent: None,
|
||||||
|
});
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +180,10 @@ impl<'t> Parser<'t> {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
self.event(Event::Token { kind, n_raw_tokens: 1 });
|
self.event(Event::Token {
|
||||||
|
kind,
|
||||||
|
n_raw_tokens: 1,
|
||||||
|
});
|
||||||
kind
|
kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +225,10 @@ pub(crate) struct ErrorBuilder<'p, 't: 'p> {
|
||||||
|
|
||||||
impl<'t, 'p> ErrorBuilder<'p, 't> {
|
impl<'t, 'p> ErrorBuilder<'p, 't> {
|
||||||
fn new(parser: &'p mut Parser<'t>) -> Self {
|
fn new(parser: &'p mut Parser<'t>) -> Self {
|
||||||
ErrorBuilder { message: None, parser }
|
ErrorBuilder {
|
||||||
|
message: None,
|
||||||
|
parser,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message<M: Into<String>>(mut self, m: M) -> Self {
|
pub fn message<M: Into<String>>(mut self, m: M) -> Self {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use {Token, File, FileBuilder, Sink, SyntaxKind};
|
use {File, FileBuilder, Sink, SyntaxKind, Token};
|
||||||
|
|
||||||
use syntax_kinds::*;
|
use syntax_kinds::*;
|
||||||
use tree::TOMBSTONE;
|
use tree::TOMBSTONE;
|
||||||
|
@ -6,17 +6,12 @@ use tree::TOMBSTONE;
|
||||||
mod event_parser;
|
mod event_parser;
|
||||||
use self::event_parser::Event;
|
use self::event_parser::Event;
|
||||||
|
|
||||||
|
|
||||||
pub fn parse(text: String, tokens: &[Token]) -> File {
|
pub fn parse(text: String, tokens: &[Token]) -> File {
|
||||||
let events = event_parser::parse(&text, tokens);
|
let events = event_parser::parse(&text, tokens);
|
||||||
from_events_to_file(text, tokens, events)
|
from_events_to_file(text, tokens, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_events_to_file(
|
fn from_events_to_file(text: String, tokens: &[Token], events: Vec<Event>) -> File {
|
||||||
text: String,
|
|
||||||
tokens: &[Token],
|
|
||||||
events: Vec<Event>,
|
|
||||||
) -> File {
|
|
||||||
let mut builder = FileBuilder::new(text);
|
let mut builder = FileBuilder::new(text);
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
|
||||||
|
@ -26,18 +21,23 @@ fn from_events_to_file(
|
||||||
for (i, event) in events.iter().enumerate() {
|
for (i, event) in events.iter().enumerate() {
|
||||||
if holes.last() == Some(&i) {
|
if holes.last() == Some(&i) {
|
||||||
holes.pop();
|
holes.pop();
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
&Event::Start { kind: TOMBSTONE, .. } => (),
|
&Event::Start {
|
||||||
|
kind: TOMBSTONE, ..
|
||||||
|
} => (),
|
||||||
|
|
||||||
&Event::Start { .. } => {
|
&Event::Start { .. } => {
|
||||||
forward_parents.clear();
|
forward_parents.clear();
|
||||||
let mut idx = i;
|
let mut idx = i;
|
||||||
loop {
|
loop {
|
||||||
let (kind, fwd) = match events[idx] {
|
let (kind, fwd) = match events[idx] {
|
||||||
Event::Start { kind, forward_parent } => (kind, forward_parent),
|
Event::Start {
|
||||||
|
kind,
|
||||||
|
forward_parent,
|
||||||
|
} => (kind, forward_parent),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
forward_parents.push((idx, kind));
|
forward_parents.push((idx, kind));
|
||||||
|
@ -64,8 +64,11 @@ fn from_events_to_file(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.finish_internal()
|
builder.finish_internal()
|
||||||
},
|
}
|
||||||
&Event::Token { kind: _, mut n_raw_tokens } => loop {
|
&Event::Token {
|
||||||
|
kind: _,
|
||||||
|
mut n_raw_tokens,
|
||||||
|
} => loop {
|
||||||
let token = tokens[idx];
|
let token = tokens[idx];
|
||||||
if !is_insignificant(token.kind) {
|
if !is_insignificant(token.kind) {
|
||||||
n_raw_tokens -= 1;
|
n_raw_tokens -= 1;
|
||||||
|
@ -76,8 +79,7 @@ fn from_events_to_file(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&Event::Error { ref message } =>
|
&Event::Error { ref message } => builder.error().message(message.clone()).emit(),
|
||||||
builder.error().message(message.clone()).emit(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.finish()
|
builder.finish()
|
||||||
|
|
|
@ -64,7 +64,6 @@ impl ops::SubAssign<TextUnit> for TextUnit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct TextRange {
|
pub struct TextRange {
|
||||||
start: TextUnit,
|
start: TextUnit,
|
||||||
|
@ -83,7 +82,6 @@ impl fmt::Display for TextRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl TextRange {
|
impl TextRange {
|
||||||
pub fn empty() -> TextRange {
|
pub fn empty() -> TextRange {
|
||||||
TextRange::from_to(TextUnit::new(0), TextUnit::new(0))
|
TextRange::from_to(TextUnit::new(0), TextUnit::new(0))
|
||||||
|
@ -91,7 +89,10 @@ impl TextRange {
|
||||||
|
|
||||||
pub fn from_to(from: TextUnit, to: TextUnit) -> TextRange {
|
pub fn from_to(from: TextUnit, to: TextUnit) -> TextRange {
|
||||||
assert!(from <= to, "Invalid text range [{}; {})", from, to);
|
assert!(from <= to, "Invalid text range [{}; {})", from, to);
|
||||||
TextRange { start: from, end: to }
|
TextRange {
|
||||||
|
start: from,
|
||||||
|
end: to,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_len(from: TextUnit, len: TextUnit) -> TextRange {
|
pub fn from_len(from: TextUnit, len: TextUnit) -> TextRange {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use {SyntaxKind, TextUnit, TextRange};
|
use {SyntaxKind, TextRange, TextUnit};
|
||||||
use super::{NodeData, SyntaxErrorData, NodeIdx, File};
|
use super::{File, NodeData, NodeIdx, SyntaxErrorData};
|
||||||
|
|
||||||
pub trait Sink {
|
pub trait Sink {
|
||||||
fn leaf(&mut self, kind: SyntaxKind, len: TextUnit);
|
fn leaf(&mut self, kind: SyntaxKind, len: TextUnit);
|
||||||
|
@ -8,7 +8,6 @@ pub trait Sink {
|
||||||
fn error(&mut self) -> ErrorBuilder;
|
fn error(&mut self) -> ErrorBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct FileBuilder {
|
pub struct FileBuilder {
|
||||||
text: String,
|
text: String,
|
||||||
nodes: Vec<NodeData>,
|
nodes: Vec<NodeData>,
|
||||||
|
@ -48,9 +47,9 @@ impl Sink for FileBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_internal(&mut self) {
|
fn finish_internal(&mut self) {
|
||||||
let (id, _) = self.in_progress.pop().expect(
|
let (id, _) = self.in_progress
|
||||||
"trying to complete a node, but there are no in-progress nodes"
|
.pop()
|
||||||
);
|
.expect("trying to complete a node, but there are no in-progress nodes");
|
||||||
if !self.in_progress.is_empty() {
|
if !self.in_progress.is_empty() {
|
||||||
self.add_len(id);
|
self.add_len(id);
|
||||||
}
|
}
|
||||||
|
@ -76,11 +75,14 @@ impl FileBuilder {
|
||||||
assert!(
|
assert!(
|
||||||
self.in_progress.is_empty(),
|
self.in_progress.is_empty(),
|
||||||
"some nodes in FileBuilder are unfinished: {:?}",
|
"some nodes in FileBuilder are unfinished: {:?}",
|
||||||
self.in_progress.iter().map(|&(idx, _)| self.nodes[idx].kind)
|
self.in_progress
|
||||||
|
.iter()
|
||||||
|
.map(|&(idx, _)| self.nodes[idx].kind)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.pos, (self.text.len() as u32).into(),
|
self.pos,
|
||||||
|
(self.text.len() as u32).into(),
|
||||||
"nodes in FileBuilder do not cover the whole file"
|
"nodes in FileBuilder do not cover the whole file"
|
||||||
);
|
);
|
||||||
File {
|
File {
|
||||||
|
@ -100,7 +102,6 @@ impl FileBuilder {
|
||||||
child.parent = Some(self.current_id());
|
child.parent = Some(self.current_id());
|
||||||
let id = self.new_node(child);
|
let id = self.new_node(child);
|
||||||
{
|
{
|
||||||
|
|
||||||
let (parent, sibling) = *self.in_progress.last().unwrap();
|
let (parent, sibling) = *self.in_progress.last().unwrap();
|
||||||
let slot = if let Some(idx) = sibling {
|
let slot = if let Some(idx) = sibling {
|
||||||
&mut self.nodes[idx].next_sibling
|
&mut self.nodes[idx].next_sibling
|
||||||
|
@ -140,12 +141,15 @@ fn grow(left: &mut TextRange, right: TextRange) {
|
||||||
|
|
||||||
pub struct ErrorBuilder<'f> {
|
pub struct ErrorBuilder<'f> {
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
builder: &'f mut FileBuilder
|
builder: &'f mut FileBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> ErrorBuilder<'f> {
|
impl<'f> ErrorBuilder<'f> {
|
||||||
fn new(builder: &'f mut FileBuilder) -> Self {
|
fn new(builder: &'f mut FileBuilder) -> Self {
|
||||||
ErrorBuilder { message: None, builder }
|
ErrorBuilder {
|
||||||
|
message: None,
|
||||||
|
builder,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message<M: Into<String>>(mut self, m: M) -> Self {
|
pub fn message<M: Into<String>>(mut self, m: M) -> Self {
|
||||||
|
@ -156,6 +160,10 @@ impl<'f> ErrorBuilder<'f> {
|
||||||
pub fn emit(self) {
|
pub fn emit(self) {
|
||||||
let message = self.message.expect("Error message not set");
|
let message = self.message.expect("Error message not set");
|
||||||
let &(node, after_child) = self.builder.in_progress.last().unwrap();
|
let &(node, after_child) = self.builder.in_progress.last().unwrap();
|
||||||
self.builder.errors.push(SyntaxErrorData { node, message, after_child })
|
self.builder.errors.push(SyntaxErrorData {
|
||||||
|
node,
|
||||||
|
message,
|
||||||
|
after_child,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use text::{TextUnit, TextRange};
|
use text::{TextRange, TextUnit};
|
||||||
use syntax_kinds::syntax_info;
|
use syntax_kinds::syntax_info;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -11,15 +11,10 @@ pub use self::file_builder::{FileBuilder, Sink};
|
||||||
pub struct SyntaxKind(pub(crate) u32);
|
pub struct SyntaxKind(pub(crate) u32);
|
||||||
|
|
||||||
pub(crate) const EOF: SyntaxKind = SyntaxKind(!0);
|
pub(crate) const EOF: SyntaxKind = SyntaxKind(!0);
|
||||||
pub(crate) const EOF_INFO: SyntaxInfo = SyntaxInfo {
|
pub(crate) const EOF_INFO: SyntaxInfo = SyntaxInfo { name: "EOF" };
|
||||||
name: "EOF"
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) const TOMBSTONE: SyntaxKind = SyntaxKind(!0 - 1);
|
pub(crate) const TOMBSTONE: SyntaxKind = SyntaxKind(!0 - 1);
|
||||||
pub(crate) const TOMBSTONE_INFO: SyntaxInfo = SyntaxInfo {
|
pub(crate) const TOMBSTONE_INFO: SyntaxInfo = SyntaxInfo { name: "TOMBSTONE" };
|
||||||
name: "TOMBSTONE"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
impl SyntaxKind {
|
impl SyntaxKind {
|
||||||
fn info(self) -> &'static SyntaxInfo {
|
fn info(self) -> &'static SyntaxInfo {
|
||||||
|
@ -38,7 +33,6 @@ impl fmt::Debug for SyntaxKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) struct SyntaxInfo {
|
pub(crate) struct SyntaxInfo {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
}
|
}
|
||||||
|
@ -58,7 +52,10 @@ pub struct File {
|
||||||
impl File {
|
impl File {
|
||||||
pub fn root<'f>(&'f self) -> Node<'f> {
|
pub fn root<'f>(&'f self) -> Node<'f> {
|
||||||
assert!(!self.nodes.is_empty());
|
assert!(!self.nodes.is_empty());
|
||||||
Node { file: self, idx: NodeIdx(0) }
|
Node {
|
||||||
|
file: self,
|
||||||
|
idx: NodeIdx(0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,14 +83,17 @@ impl<'f> Node<'f> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> Children<'f> {
|
pub fn children(&self) -> Children<'f> {
|
||||||
Children { next: self.as_node(self.data().first_child) }
|
Children {
|
||||||
|
next: self.as_node(self.data().first_child),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors(&self) -> SyntaxErrors<'f> {
|
pub fn errors(&self) -> SyntaxErrors<'f> {
|
||||||
let pos = self.file.errors.iter().position(|e| e.node == self.idx);
|
let pos = self.file.errors.iter().position(|e| e.node == self.idx);
|
||||||
let next = pos
|
let next = pos.map(|i| ErrorIdx(i as u32)).map(|idx| SyntaxError {
|
||||||
.map(|i| ErrorIdx(i as u32))
|
file: self.file,
|
||||||
.map(|idx| SyntaxError { file: self.file, idx });
|
idx,
|
||||||
|
});
|
||||||
SyntaxErrors { next }
|
SyntaxErrors { next }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,10 @@ impl<'f> Node<'f> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_node(&self, idx: Option<NodeIdx>) -> Option<Node<'f>> {
|
fn as_node(&self, idx: Option<NodeIdx>) -> Option<Node<'f>> {
|
||||||
idx.map(|idx| Node { file: self.file, idx })
|
idx.map(|idx| Node {
|
||||||
|
file: self.file,
|
||||||
|
idx,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +121,7 @@ impl<'f> cmp::PartialEq<Node<'f>> for Node<'f> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> cmp::Eq for Node<'f> {
|
impl<'f> cmp::Eq for Node<'f> {}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct SyntaxError<'f> {
|
pub struct SyntaxError<'f> {
|
||||||
|
@ -134,7 +136,10 @@ impl<'f> SyntaxError<'f> {
|
||||||
|
|
||||||
pub fn after_child(&self) -> Option<Node<'f>> {
|
pub fn after_child(&self) -> Option<Node<'f>> {
|
||||||
let idx = self.data().after_child?;
|
let idx = self.data().after_child?;
|
||||||
Some(Node { file: self.file, idx })
|
Some(Node {
|
||||||
|
file: self.file,
|
||||||
|
idx,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data(&self) -> &'f SyntaxErrorData {
|
fn data(&self) -> &'f SyntaxErrorData {
|
||||||
|
@ -148,7 +153,7 @@ impl<'f> SyntaxError<'f> {
|
||||||
}
|
}
|
||||||
let result = SyntaxError {
|
let result = SyntaxError {
|
||||||
file: self.file,
|
file: self.file,
|
||||||
idx: ErrorIdx(next_idx)
|
idx: ErrorIdx(next_idx),
|
||||||
};
|
};
|
||||||
if result.data().node != self.data().node {
|
if result.data().node != self.data().node {
|
||||||
return None;
|
return None;
|
||||||
|
@ -185,7 +190,6 @@ impl<'f> Iterator for SyntaxErrors<'f> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
struct NodeIdx(u32);
|
struct NodeIdx(u32);
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,15 @@ extern crate testutils;
|
||||||
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use libsyntax2::{Token, tokenize};
|
use libsyntax2::{tokenize, Token};
|
||||||
use testutils::dir_tests;
|
use testutils::dir_tests;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lexer_tests() {
|
fn lexer_tests() {
|
||||||
dir_tests(
|
dir_tests(&["lexer"], |text| {
|
||||||
&["lexer"],
|
|
||||||
|text| {
|
|
||||||
let tokens = tokenize(text);
|
let tokens = tokenize(text);
|
||||||
dump_tokens(&tokens, text)
|
dump_tokens(&tokens, text)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_tokens(tokens: &[Token], text: &str) -> String {
|
fn dump_tokens(tokens: &[Token], text: &str) -> String {
|
||||||
|
|
|
@ -2,18 +2,15 @@ extern crate file;
|
||||||
extern crate libsyntax2;
|
extern crate libsyntax2;
|
||||||
extern crate testutils;
|
extern crate testutils;
|
||||||
|
|
||||||
use libsyntax2::{tokenize, parse};
|
use libsyntax2::{parse, tokenize};
|
||||||
use libsyntax2::utils::dump_tree;
|
use libsyntax2::utils::dump_tree;
|
||||||
use testutils::dir_tests;
|
use testutils::dir_tests;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_tests() {
|
fn parser_tests() {
|
||||||
dir_tests(
|
dir_tests(&["parser/ok", "parser/err"], |text| {
|
||||||
&["parser/ok", "parser/err"],
|
|
||||||
|text| {
|
|
||||||
let tokens = tokenize(text);
|
let tokens = tokenize(text);
|
||||||
let file = parse(text.to_string(), &tokens);
|
let file = parse(text.to_string(), &tokens);
|
||||||
dump_tree(&file)
|
dump_tree(&file)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue