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