Initial commit, implements parser.
This commit is contained in:
commit
f1af568cb8
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
.idea/
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "seraph_script"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Deukhoofd <Deukhoofd@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = "0.10.0"
|
|
@ -0,0 +1,99 @@
|
||||||
|
letter ::= 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
|
||||||
|
| 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N'
|
||||||
|
| 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U'
|
||||||
|
| 'V' | 'W' | 'X' | 'Y' | 'Z' | 'a' | 'b'
|
||||||
|
| 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i'
|
||||||
|
| 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p'
|
||||||
|
| 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w'
|
||||||
|
| 'x' | 'y' | 'z' ;
|
||||||
|
|
||||||
|
all_characters ::= ;
|
||||||
|
digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
|
||||||
|
hexadecimal_digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
|
||||||
|
'8' | '9' | 'A' | 'a' | 'B' | 'b' | 'C' | 'c' |
|
||||||
|
'D' | 'd' | 'E' | 'e' | 'F' | 'f';
|
||||||
|
octal_digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7';
|
||||||
|
binary_digit ::= '0' | '1';
|
||||||
|
|
||||||
|
character ::= letter | digit | '_' ;
|
||||||
|
|
||||||
|
identifier ::= (letter | '_') { character };
|
||||||
|
|
||||||
|
float ::= digit { digit } '.' digit {digit};
|
||||||
|
dec_integer ::= ['0D' | '0d'] digit {digit};
|
||||||
|
hex_integer ::= ('0X'|'0x') hexadecimal_digit {hexadecimal_digit};
|
||||||
|
octal_integer ::= ('0O'|'0o') octal_digit {octal_digit};
|
||||||
|
binary_integer ::= ('0B'|'0b') binary_digit {binary_digit};
|
||||||
|
integer ::= dec_integer | hex_integer | octal_integer | binary_integer;
|
||||||
|
number ::= integer | float;
|
||||||
|
|
||||||
|
string ::= ('\'' {all_characters} '\'' | '\"' {all_characters} '\"' | '\"\"\"' {all_characters} '\"\"\"');
|
||||||
|
|
||||||
|
assignop ::= '=' | '+=' | '-=' | '*=' | '/=' | '|=' | '&=' | '^=' | '%=' | '**=' | '<<=' | '>>=' | '>>>=';
|
||||||
|
logicop ::= '&&' | '||' | '^^' | 'and' | 'or' | 'xor';
|
||||||
|
compop ::= '==' | '!=' | '<' | '<=' | '>' | '>=' | 'is' | '!is';
|
||||||
|
mathop ::= '+' | '-' | '*' | '/' | '%' | '**';
|
||||||
|
bitop ::= '&' | '|' | '^' | '<<' | '>>' | '>>>';
|
||||||
|
|
||||||
|
|
||||||
|
primtype ::= 'void' | 'int' | 'int8' | 'int16' | 'int32' | 'int64' | 'uint' | 'uint8' | 'uint16' |
|
||||||
|
'uint32' | 'uint64' | 'float' | 'double' | 'bool';
|
||||||
|
datatype ::= (identifier | primtype | 'auto');
|
||||||
|
scope ::= ['::'] {identifier '::'} [identifier ['<' type {',' type} '>'] '::'];
|
||||||
|
type ::= ['const'] scope datatype ['<' type {',' type} '>'] { ('[' ']') | ('@' ['const']) };
|
||||||
|
# ternary is defined further below due to a circular dependency: ternary->expr->exprterm->initlist->assign
|
||||||
|
assign ::= ternary [ assignop assign ];
|
||||||
|
initlist ::= '{' [assign | initlist] {',' [assign | initlist]} '}';
|
||||||
|
exprpreop ::= '-' | '+' | '!' | '++' | '--' | '~' | '@';
|
||||||
|
arglist ::= '(' [identifier ':'] assign {',' [identifier ':'] assign} ')';
|
||||||
|
funccall ::= scope identifier arglist;
|
||||||
|
constructcall ::= type arglist;
|
||||||
|
varaccess ::= scope | identifier;
|
||||||
|
cast ::= 'cast' '<' type '>' '(' assign ')';
|
||||||
|
literal ::= number | string | 'true' | 'false' | 'null';
|
||||||
|
typemod ::= ['&' ['in' | 'out' | 'inout']];
|
||||||
|
# statblock is defined further below, as statements are higher level than expressions.
|
||||||
|
lambda ::= 'function' '(' [[type typemod] identifier {',' [type typemod] identifier}] ')' statblock;
|
||||||
|
|
||||||
|
exprvalue ::= 'void' | constructcall | funccall | varaccess | cast | literal | '(' assign ')' | lambda;
|
||||||
|
exprpostop ::= ('.' (funccall | identifier)) | ('[' [identifier ':'] assign {',' [identifier ':' assign} ']') | arglist | '++' | '--';
|
||||||
|
exprterm ::= ([type '='] initlist) | ({exprpreop} exprvalue {exprpostop});
|
||||||
|
expr ::= exprterm {(mathop | compop | logicop | bitop) exprterm};
|
||||||
|
ternary ::= expr ['?' assign : assign];
|
||||||
|
|
||||||
|
return ::= 'return' [assign] ';';
|
||||||
|
exprstat ::= assign ';';
|
||||||
|
continue ::= 'continue' ';';
|
||||||
|
break ::= 'break' ';';
|
||||||
|
|
||||||
|
# As these are all statements using other statements, they use the statement and statblock types defined further below.
|
||||||
|
if ::= 'if' '(' assign ')' statement ['else' statement];
|
||||||
|
for ::= 'for' '(' (var | exprstat) exprstat [assign {',' assign}] ')' statement;
|
||||||
|
while ::= 'while' '(' assign ')' statement;
|
||||||
|
dowhile ::= 'do' statement 'while' '(' assign ')' ';';
|
||||||
|
try ::= 'try' statblock 'catch' statblock;
|
||||||
|
case ::= (('case' expr) | 'default') ':' {statement};
|
||||||
|
switch ::= 'switch' '(' assign ')' '{' {case} '}';
|
||||||
|
|
||||||
|
statement ::= (if | for | while | return | statblock | break | continue | dowhile | switch | exprstat | try );
|
||||||
|
var ::= ['private'|'protected'] type identifier [( '=' (initlist | expr)) | arglist] {',' identifier [( '=' (initlist | expr)) | arglist]} ';';
|
||||||
|
statblock ::= '{' {var | statement} '}';
|
||||||
|
|
||||||
|
funcattr ::= {'override' | 'final' | 'explicit' | 'property'};
|
||||||
|
paramlist ::= '(' ['void' | (type typemod [identifier] ['=' expr] {',' type typemod [identifier] ['=' expr]})] ')';
|
||||||
|
|
||||||
|
virtprop ::= ['private' | 'protected'] type ['&'] identifier '{' {('get' | 'set') ['const'] funcattr (statblock | ';')} '}';
|
||||||
|
func ::= {'shared' | 'external'} ['private' | 'protected'] [((type ['&']) | '~')] identifier paramlist ['const'] funcattr (';' | statblock);
|
||||||
|
funcdef ::= {'external' | 'shared'} 'funcdef' type ['&'] identifier paramlist ';'
|
||||||
|
class ::= {'shared' | 'abstract' | 'final' | 'external'} 'class' identifier
|
||||||
|
(';' | ([':' identifier {',' identifier}] '{' {virtprop | func | var | funcdef | class} '}'));
|
||||||
|
mixin ::= 'mixin' class;
|
||||||
|
enum ::= {'shared' | 'external'} 'enum' identifier [ ':' primtype ] (';' | ('{' identifier ['=' expr] {',' identifier ['=' expr]} '}'));
|
||||||
|
import ::= 'import' type ['&'] identifier paramlist funcattr 'from' string ';';
|
||||||
|
typedef ::= 'typedef' (primtype | identifier) identifier ';';
|
||||||
|
|
||||||
|
interfacemethod ::= type ['&'] identifier paramlist ['const'] ';';
|
||||||
|
interface ::= {'external' | 'shared'} 'interface' identifier (';' | ([':' identifier {',' identifier}] '{' {virtprop | interfacemethod} '}'));
|
||||||
|
|
||||||
|
namespace ::= 'namespace' identifier '{' script '}';
|
||||||
|
script ::= {import | enum | typedef | class | mixin | interface | funcdef | virtprop | var | func | namespace | ';'};
|
|
@ -0,0 +1,4 @@
|
||||||
|
/// The size integers use internally to store literals and do compile time calculations.
|
||||||
|
pub type LiteralInt = i64;
|
||||||
|
/// The size floating point numbers use internally to store literals and do compile time calculations.
|
||||||
|
pub type LiteralFloat = f64;
|
|
@ -0,0 +1,5 @@
|
||||||
|
#![feature(concat_idents)]
|
||||||
|
#![feature(exclusive_range_pattern)]
|
||||||
|
|
||||||
|
pub(crate) mod defines;
|
||||||
|
pub mod parsing;
|
|
@ -0,0 +1,242 @@
|
||||||
|
use crate::defines::{LiteralFloat, LiteralInt};
|
||||||
|
use crate::parsing::lex_tokens::LexToken;
|
||||||
|
use itertools::MultiPeek;
|
||||||
|
use std::str::Chars;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_decimal_value(c: char) -> Option<LiteralInt> {
|
||||||
|
match c {
|
||||||
|
'0' => Some(0),
|
||||||
|
'1' => Some(1),
|
||||||
|
'2' => Some(2),
|
||||||
|
'3' => Some(3),
|
||||||
|
'4' => Some(4),
|
||||||
|
'5' => Some(5),
|
||||||
|
'6' => Some(6),
|
||||||
|
'7' => Some(7),
|
||||||
|
'8' => Some(8),
|
||||||
|
'9' => Some(9),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_numeric_default(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
let mut int_value: LiteralInt = 0;
|
||||||
|
let mut decimal_value: LiteralInt = 0;
|
||||||
|
let mut exponent_value: LiteralInt = 0;
|
||||||
|
let mut decimal_length: LiteralInt = 0;
|
||||||
|
let mut is_decimal = false;
|
||||||
|
let mut is_exponent = false;
|
||||||
|
let mut is_reading = true;
|
||||||
|
|
||||||
|
let mut c: Option<char> = chars.peek().cloned();
|
||||||
|
while c.is_some() && is_reading {
|
||||||
|
let v = get_decimal_value(c.unwrap());
|
||||||
|
match v {
|
||||||
|
None => {
|
||||||
|
if c.unwrap() == '_' {
|
||||||
|
chars.next();
|
||||||
|
c = chars.peek().cloned();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !is_decimal && c.unwrap() == '.' {
|
||||||
|
is_decimal = true;
|
||||||
|
chars.next();
|
||||||
|
c = chars.peek().cloned();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if is_decimal && c.unwrap() == 'e' || c.unwrap() == 'E' {
|
||||||
|
is_decimal = false;
|
||||||
|
is_exponent = true;
|
||||||
|
chars.next();
|
||||||
|
c = chars.peek().cloned();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c = chars.peek().cloned();
|
||||||
|
is_reading = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(i) => {
|
||||||
|
chars.next();
|
||||||
|
if is_decimal {
|
||||||
|
decimal_value *= 10;
|
||||||
|
decimal_value += i;
|
||||||
|
decimal_length += 1;
|
||||||
|
} else if is_exponent {
|
||||||
|
exponent_value *= 10;
|
||||||
|
exponent_value += i;
|
||||||
|
} else {
|
||||||
|
int_value *= 10;
|
||||||
|
int_value += i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = chars.peek().cloned();
|
||||||
|
}
|
||||||
|
chars.reset_peek();
|
||||||
|
if is_decimal || is_exponent {
|
||||||
|
let mut val = int_value as LiteralFloat
|
||||||
|
+ (decimal_value as LiteralFloat / 10_i64.pow(decimal_length as u32) as LiteralFloat);
|
||||||
|
if is_exponent {
|
||||||
|
val *= exponent_value.pow(10) as LiteralFloat;
|
||||||
|
}
|
||||||
|
LexToken::FloatLiteral(val)
|
||||||
|
} else {
|
||||||
|
LexToken::IntegerLiteral(int_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_hexadecimal_value(c: char) -> Option<LiteralInt> {
|
||||||
|
match c {
|
||||||
|
'0' => Some(0),
|
||||||
|
'1' => Some(1),
|
||||||
|
'2' => Some(2),
|
||||||
|
'3' => Some(3),
|
||||||
|
'4' => Some(4),
|
||||||
|
'5' => Some(5),
|
||||||
|
'6' => Some(6),
|
||||||
|
'7' => Some(7),
|
||||||
|
'8' => Some(8),
|
||||||
|
'9' => Some(9),
|
||||||
|
'A' | 'a' => Some(10),
|
||||||
|
'B' | 'b' => Some(11),
|
||||||
|
'C' | 'c' => Some(12),
|
||||||
|
'D' | 'd' => Some(13),
|
||||||
|
'E' | 'e' => Some(14),
|
||||||
|
'F' | 'f' => Some(15),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_numeric_hexadecimal(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
let mut int_value: LiteralInt = 0;
|
||||||
|
let mut reading = true;
|
||||||
|
let mut n = chars.peek().cloned();
|
||||||
|
while n.is_some() && reading {
|
||||||
|
match get_hexadecimal_value(n.unwrap()) {
|
||||||
|
Some(i) => {
|
||||||
|
int_value <<= 4;
|
||||||
|
int_value += i;
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if n.unwrap() == '_' {
|
||||||
|
chars.next();
|
||||||
|
} else {
|
||||||
|
reading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = chars.peek().cloned();
|
||||||
|
}
|
||||||
|
LexToken::IntegerLiteral(int_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_octal_value(c: char) -> Option<LiteralInt> {
|
||||||
|
match c {
|
||||||
|
'0' => Some(0),
|
||||||
|
'1' => Some(1),
|
||||||
|
'2' => Some(2),
|
||||||
|
'3' => Some(3),
|
||||||
|
'4' => Some(4),
|
||||||
|
'5' => Some(5),
|
||||||
|
'6' => Some(6),
|
||||||
|
'7' => Some(7),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_numeric_octal(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
let mut int_value: LiteralInt = 0;
|
||||||
|
let mut reading = true;
|
||||||
|
let mut n = chars.peek().cloned();
|
||||||
|
while n.is_some() && reading {
|
||||||
|
match get_octal_value(n.unwrap()) {
|
||||||
|
Some(i) => {
|
||||||
|
int_value <<= 3;
|
||||||
|
int_value += i;
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if n.unwrap() == '_' {
|
||||||
|
chars.next();
|
||||||
|
} else {
|
||||||
|
reading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = chars.peek().cloned();
|
||||||
|
}
|
||||||
|
LexToken::IntegerLiteral(int_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_binary_value(c: char) -> Option<LiteralInt> {
|
||||||
|
match c {
|
||||||
|
'0' => Some(0),
|
||||||
|
'1' => Some(1),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_numeric_binary(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
let mut int_value: LiteralInt = 0;
|
||||||
|
let mut reading = true;
|
||||||
|
let mut n = chars.peek().cloned();
|
||||||
|
while n.is_some() && reading {
|
||||||
|
match get_binary_value(n.unwrap()) {
|
||||||
|
Some(i) => {
|
||||||
|
int_value <<= 1;
|
||||||
|
int_value += i;
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if n.unwrap() == '_' {
|
||||||
|
chars.next();
|
||||||
|
} else {
|
||||||
|
reading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = chars.peek().cloned();
|
||||||
|
}
|
||||||
|
LexToken::IntegerLiteral(int_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn lex_numeric(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
chars.reset_peek();
|
||||||
|
if chars.peek() == Some(&'0') {
|
||||||
|
match chars.peek() {
|
||||||
|
Some(&'D') | Some(&'d') => {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
return lex_numeric_default(chars);
|
||||||
|
}
|
||||||
|
Some(&'X') | Some(&'x') => {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
return lex_numeric_hexadecimal(chars);
|
||||||
|
}
|
||||||
|
Some(&'O') | Some(&'o') => {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
return lex_numeric_octal(chars);
|
||||||
|
}
|
||||||
|
Some(&'B') | Some(&'b') => {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
return lex_numeric_binary(chars);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chars.reset_peek();
|
||||||
|
lex_numeric_default(chars)
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
use crate::defines::{LiteralFloat, LiteralInt};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum LexToken {
|
||||||
|
EndOfFile,
|
||||||
|
WhiteSpace,
|
||||||
|
Identifier(String),
|
||||||
|
IntegerLiteral(LiteralInt),
|
||||||
|
FloatLiteral(LiteralFloat),
|
||||||
|
StringLiteral(String),
|
||||||
|
|
||||||
|
Semicolon,
|
||||||
|
Colon,
|
||||||
|
OpenBracket,
|
||||||
|
CloseBracket,
|
||||||
|
OpenCurlyBracket,
|
||||||
|
CloseCurlyBracket,
|
||||||
|
OpenBlockBracket,
|
||||||
|
CloseBlockBracket,
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
AndKeyword,
|
||||||
|
AbstractKeyword,
|
||||||
|
AutoKeyword,
|
||||||
|
BoolKeyword,
|
||||||
|
BreakKeyword,
|
||||||
|
CaseKeyword,
|
||||||
|
CastKeyword,
|
||||||
|
CatchKeyword,
|
||||||
|
ClassKeyword,
|
||||||
|
ConstKeyword,
|
||||||
|
ContinueKeyword,
|
||||||
|
DefaultKeyword,
|
||||||
|
DoKeyword,
|
||||||
|
DoubleKeyword,
|
||||||
|
ElseKeyword,
|
||||||
|
EnumKeyword,
|
||||||
|
ExplicitKeyword,
|
||||||
|
ExternalKeyword,
|
||||||
|
FalseKeyword,
|
||||||
|
FinalKeyword,
|
||||||
|
FloatKeyword,
|
||||||
|
ForKeyword,
|
||||||
|
FromKeyword,
|
||||||
|
FuncDefKeyword,
|
||||||
|
FunctionKeyword,
|
||||||
|
GetKeyword,
|
||||||
|
IfKeyword,
|
||||||
|
ImportKeyword,
|
||||||
|
InKeyword,
|
||||||
|
InOutKeyword,
|
||||||
|
IntKeyword,
|
||||||
|
InterfaceKeyword,
|
||||||
|
Int8Keyword,
|
||||||
|
Int16Keyword,
|
||||||
|
Int32Keyword,
|
||||||
|
Int64Keyword,
|
||||||
|
IsKeyword,
|
||||||
|
MixinKeyword,
|
||||||
|
NamespaceKeyword,
|
||||||
|
NotKeyword,
|
||||||
|
NullKeyword,
|
||||||
|
OrKeyword,
|
||||||
|
OutKeyword,
|
||||||
|
OverrideKeyword,
|
||||||
|
PrivateKeyword,
|
||||||
|
PropertyKeyword,
|
||||||
|
ProtectedKeyword,
|
||||||
|
ReturnKeyword,
|
||||||
|
SetKeyword,
|
||||||
|
SharedKeyword,
|
||||||
|
SuperKeyword,
|
||||||
|
SwitchKeyword,
|
||||||
|
ThisKeyword,
|
||||||
|
TrueKeyword,
|
||||||
|
TryKeyword,
|
||||||
|
TypeDefKeyword,
|
||||||
|
UintKeyword,
|
||||||
|
Uint8Keyword,
|
||||||
|
Uint16Keyword,
|
||||||
|
Uint32Keyword,
|
||||||
|
Uint64Keyword,
|
||||||
|
VoidKeyword,
|
||||||
|
WhileKeyword,
|
||||||
|
XorKeyword,
|
||||||
|
|
||||||
|
// AssignOp
|
||||||
|
Equals,
|
||||||
|
PlusEquals,
|
||||||
|
MinusEquals,
|
||||||
|
StarEquals,
|
||||||
|
SlashEquals,
|
||||||
|
LineEquals,
|
||||||
|
AmpersandEquals,
|
||||||
|
RoofEquals,
|
||||||
|
PercentEquals,
|
||||||
|
StarStarEquals,
|
||||||
|
LeftLeftEquals,
|
||||||
|
RightRightEquals,
|
||||||
|
RightRightRightEquals,
|
||||||
|
|
||||||
|
// LogicOp
|
||||||
|
AmpersandAmpersand,
|
||||||
|
LineLine,
|
||||||
|
RoofRoof,
|
||||||
|
|
||||||
|
// CompOp
|
||||||
|
EqualsEquals,
|
||||||
|
NotEquals,
|
||||||
|
NotIsKeyword,
|
||||||
|
GreaterThan,
|
||||||
|
GreaterThanEquals,
|
||||||
|
LessThan,
|
||||||
|
LessThanEquals,
|
||||||
|
|
||||||
|
// MathOp
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Percent,
|
||||||
|
StarStar,
|
||||||
|
|
||||||
|
// BitOp
|
||||||
|
Ampersand,
|
||||||
|
VerticalLine,
|
||||||
|
Roof,
|
||||||
|
LeftLeft,
|
||||||
|
RightRight,
|
||||||
|
RightRightRight,
|
||||||
|
|
||||||
|
// ExprPreOp
|
||||||
|
ExclamationMark,
|
||||||
|
PlusPlus,
|
||||||
|
MinusMinus,
|
||||||
|
Tilde,
|
||||||
|
AtSymbol,
|
||||||
|
}
|
|
@ -0,0 +1,350 @@
|
||||||
|
use super::lex_numerical::lex_numeric;
|
||||||
|
use crate::parsing::lex_tokens::LexToken;
|
||||||
|
use itertools::{Itertools, MultiPeek};
|
||||||
|
use std::str::Chars;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_and_consume(chars: &mut MultiPeek<Chars>, eq: LexToken) -> LexToken {
|
||||||
|
chars.next();
|
||||||
|
eq
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_eq_or(chars: &mut MultiPeek<Chars>, eq: LexToken, or: LexToken) -> LexToken {
|
||||||
|
chars.next();
|
||||||
|
if let Some('=') = chars.peek() {
|
||||||
|
chars.next();
|
||||||
|
eq
|
||||||
|
} else {
|
||||||
|
or
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lex_eq_rep_or(
|
||||||
|
chars: &mut MultiPeek<Chars>,
|
||||||
|
v: char,
|
||||||
|
eq: LexToken,
|
||||||
|
rep: LexToken,
|
||||||
|
or: LexToken,
|
||||||
|
) -> LexToken {
|
||||||
|
chars.next();
|
||||||
|
return match chars.peek() {
|
||||||
|
Some(c) => {
|
||||||
|
if *c == v {
|
||||||
|
chars.next();
|
||||||
|
rep
|
||||||
|
} else if *c == '=' {
|
||||||
|
chars.next();
|
||||||
|
eq
|
||||||
|
} else {
|
||||||
|
or
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => or,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type LT = LexToken;
|
||||||
|
|
||||||
|
fn lex_keyword_or_identifier(chars: &mut MultiPeek<Chars>) -> LexToken {
|
||||||
|
let mut reading = true;
|
||||||
|
let mut length = 1;
|
||||||
|
while reading {
|
||||||
|
match chars.peek() {
|
||||||
|
Some(c) => match c {
|
||||||
|
'a'..'z' | 'A'..'Z' | '_' | '0'..'9' => {
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
reading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
reading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
chars.reset_peek();
|
||||||
|
let c: String = chars.take(length).collect();
|
||||||
|
|
||||||
|
match c.as_str() {
|
||||||
|
"and" => LT::AndKeyword,
|
||||||
|
"abstract" => LT::AbstractKeyword,
|
||||||
|
"auto" => LT::AutoKeyword,
|
||||||
|
"bool" => LT::BoolKeyword,
|
||||||
|
"break" => LT::BreakKeyword,
|
||||||
|
"case" => LT::CaseKeyword,
|
||||||
|
"cast" => LT::CastKeyword,
|
||||||
|
"catch" => LT::CatchKeyword,
|
||||||
|
"class" => LT::ClassKeyword,
|
||||||
|
"const" => LT::ConstKeyword,
|
||||||
|
"continue" => LT::ContinueKeyword,
|
||||||
|
"default" => LT::DefaultKeyword,
|
||||||
|
"do" => LT::DoKeyword,
|
||||||
|
"double" => LT::DoubleKeyword,
|
||||||
|
"else" => LT::ElseKeyword,
|
||||||
|
"enum" => LT::EnumKeyword,
|
||||||
|
"explicit" => LT::ExplicitKeyword,
|
||||||
|
"external" => LT::ExternalKeyword,
|
||||||
|
"false" => LT::FalseKeyword,
|
||||||
|
"final" => LT::FinalKeyword,
|
||||||
|
"float" => LT::FloatKeyword,
|
||||||
|
"for" => LT::ForKeyword,
|
||||||
|
"from" => LT::FromKeyword,
|
||||||
|
"funcdef" => LT::FuncDefKeyword,
|
||||||
|
"function" => LT::FunctionKeyword,
|
||||||
|
"get" => LT::GetKeyword,
|
||||||
|
"if" => LT::IfKeyword,
|
||||||
|
"import" => LT::ImportKeyword,
|
||||||
|
"in" => LT::InKeyword,
|
||||||
|
"inout" => LT::InOutKeyword,
|
||||||
|
"int" => LT::IntKeyword,
|
||||||
|
"interface" => LT::InterfaceKeyword,
|
||||||
|
"int8" => LT::Int8Keyword,
|
||||||
|
"int16" => LT::Int16Keyword,
|
||||||
|
"int32" => LT::Int32Keyword,
|
||||||
|
"int64" => LT::Int64Keyword,
|
||||||
|
"is" => LT::IsKeyword,
|
||||||
|
"mixin" => LT::MixinKeyword,
|
||||||
|
"namespace" => LT::NamespaceKeyword,
|
||||||
|
"not" => LT::NotKeyword,
|
||||||
|
"null" => LT::NullKeyword,
|
||||||
|
"or" => LT::OrKeyword,
|
||||||
|
"out" => LT::OutKeyword,
|
||||||
|
"override" => LT::OverrideKeyword,
|
||||||
|
"private" => LT::PrivateKeyword,
|
||||||
|
"property" => LT::PropertyKeyword,
|
||||||
|
"protected" => LT::ProtectedKeyword,
|
||||||
|
"return" => LT::ReturnKeyword,
|
||||||
|
"set" => LT::SetKeyword,
|
||||||
|
"shared" => LT::SharedKeyword,
|
||||||
|
"super" => LT::SuperKeyword,
|
||||||
|
"switch" => LT::SwitchKeyword,
|
||||||
|
"this" => LT::ThisKeyword,
|
||||||
|
"true" => LT::TrueKeyword,
|
||||||
|
"try" => LT::TryKeyword,
|
||||||
|
"typedef" => LT::TypeDefKeyword,
|
||||||
|
"uint" => LT::UintKeyword,
|
||||||
|
"uint8" => LT::Uint8Keyword,
|
||||||
|
"uint16" => LT::Uint16Keyword,
|
||||||
|
"uint32" => LT::Uint32Keyword,
|
||||||
|
"uint64" => LT::Uint64Keyword,
|
||||||
|
"void" => LT::VoidKeyword,
|
||||||
|
"while" => LT::WhileKeyword,
|
||||||
|
"xor" => LT::XorKeyword,
|
||||||
|
_ => LT::Identifier(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex_string(chars: &mut MultiPeek<Chars>, opening_char: &char, heredoc: bool) -> LexToken {
|
||||||
|
chars.next();
|
||||||
|
if heredoc {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
let mut length: i32 = 0;
|
||||||
|
let mut string_length = 0;
|
||||||
|
let mut last_was_control = false;
|
||||||
|
|
||||||
|
// We loop twice here. In the first loop we get the number of characters to read, the number of
|
||||||
|
// characters the string should be, and whether it's valid. This reduces the amount of allocations
|
||||||
|
// we need to do to read a string.
|
||||||
|
loop {
|
||||||
|
let p = chars.peek();
|
||||||
|
match p {
|
||||||
|
None => {
|
||||||
|
// TODO: log error. Strings need to be closed, EOF should error.
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
Some(&'\\') if !last_was_control => {
|
||||||
|
last_was_control = true;
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
if c == opening_char && !last_was_control {
|
||||||
|
if heredoc {
|
||||||
|
if chars.peek() == Some(&'\"') && chars.peek() == Some(&'\"') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
length += 1;
|
||||||
|
string_length += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
length += 1;
|
||||||
|
string_length += 1;
|
||||||
|
last_was_control = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chars.reset_peek();
|
||||||
|
let mut s: String = String::with_capacity(string_length);
|
||||||
|
for _ in 0..length {
|
||||||
|
let p = chars.next().unwrap();
|
||||||
|
match p {
|
||||||
|
'\\' => {
|
||||||
|
if last_was_control {
|
||||||
|
s.push('\\');
|
||||||
|
} else {
|
||||||
|
last_was_control = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'0' if last_was_control => s.push('\0'),
|
||||||
|
'n' if last_was_control => s.push('\n'),
|
||||||
|
'r' if last_was_control => s.push('\r'),
|
||||||
|
't' if last_was_control => s.push('\t'),
|
||||||
|
_ => s.push(p),
|
||||||
|
};
|
||||||
|
last_was_control = false;
|
||||||
|
}
|
||||||
|
assert_eq!(s.len(), string_length);
|
||||||
|
chars.reset_peek();
|
||||||
|
chars.next();
|
||||||
|
if heredoc {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
LT::StringLiteral(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lex(s: &str) -> Vec<LT> {
|
||||||
|
let mut tokens: Vec<LT> = Vec::new();
|
||||||
|
let mut chars = s.chars().multipeek();
|
||||||
|
let mut reading = true;
|
||||||
|
while reading {
|
||||||
|
let p = chars.peek().cloned();
|
||||||
|
match p {
|
||||||
|
Some(c) => match c {
|
||||||
|
' ' | '\t' | '\r' | '\n' => {
|
||||||
|
chars.next();
|
||||||
|
tokens.push(LT::WhiteSpace);
|
||||||
|
}
|
||||||
|
'=' => tokens.push(lex_eq_or(&mut chars, LT::EqualsEquals, LT::Equals)),
|
||||||
|
'+' => tokens.push(lex_eq_rep_or(
|
||||||
|
&mut chars,
|
||||||
|
'+',
|
||||||
|
LT::PlusEquals,
|
||||||
|
LT::PlusPlus,
|
||||||
|
LT::Plus,
|
||||||
|
)),
|
||||||
|
'-' => tokens.push(lex_eq_rep_or(
|
||||||
|
&mut chars,
|
||||||
|
'-',
|
||||||
|
LT::MinusEquals,
|
||||||
|
LT::MinusMinus,
|
||||||
|
LT::Minus,
|
||||||
|
)),
|
||||||
|
'*' => {
|
||||||
|
if chars.peek() == Some(&'*') {
|
||||||
|
chars.next();
|
||||||
|
tokens.push(lex_eq_or(&mut chars, LT::StarStarEquals, LT::StarStar))
|
||||||
|
} else {
|
||||||
|
tokens.push(lex_eq_or(&mut chars, LT::StarEquals, LT::Star))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'/' => tokens.push(lex_eq_or(&mut chars, LT::SlashEquals, LT::Slash)),
|
||||||
|
'%' => tokens.push(lex_eq_or(&mut chars, LT::PercentEquals, LT::Percent)),
|
||||||
|
'|' => tokens.push(lex_eq_rep_or(
|
||||||
|
&mut chars,
|
||||||
|
'|',
|
||||||
|
LT::LineEquals,
|
||||||
|
LT::LineLine,
|
||||||
|
LT::VerticalLine,
|
||||||
|
)),
|
||||||
|
'&' => tokens.push(lex_eq_rep_or(
|
||||||
|
&mut chars,
|
||||||
|
'&',
|
||||||
|
LT::AmpersandEquals,
|
||||||
|
LT::AmpersandAmpersand,
|
||||||
|
LT::Ampersand,
|
||||||
|
)),
|
||||||
|
'^' => tokens.push(lex_eq_rep_or(
|
||||||
|
&mut chars,
|
||||||
|
'^',
|
||||||
|
LT::RoofEquals,
|
||||||
|
LT::RoofRoof,
|
||||||
|
LT::Roof,
|
||||||
|
)),
|
||||||
|
'<' => {
|
||||||
|
if chars.peek() == Some(&'<') {
|
||||||
|
chars.next();
|
||||||
|
tokens.push(lex_eq_or(&mut chars, LT::LeftLeftEquals, LT::LeftLeft))
|
||||||
|
} else {
|
||||||
|
tokens.push(lex_eq_or(&mut chars, LT::LessThanEquals, LT::LessThan))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
if chars.peek() == Some(&'>') {
|
||||||
|
if chars.peek() == Some(&'>') {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
tokens.push(lex_eq_or(
|
||||||
|
&mut chars,
|
||||||
|
LT::RightRightRightEquals,
|
||||||
|
LT::RightRightRight,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
chars.next();
|
||||||
|
tokens.push(lex_eq_or(&mut chars, LT::RightRightEquals, LT::RightRight))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tokens.push(lex_eq_or(
|
||||||
|
&mut chars,
|
||||||
|
LT::GreaterThanEquals,
|
||||||
|
LT::GreaterThan,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'!' => {
|
||||||
|
let next = chars.peek();
|
||||||
|
if next == Some(&'=') {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
tokens.push(LT::NotEquals);
|
||||||
|
} else if next == Some(&'i') && chars.peek() == Some(&'s') {
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
chars.next();
|
||||||
|
tokens.push(LT::NotIsKeyword);
|
||||||
|
} else {
|
||||||
|
chars.next();
|
||||||
|
tokens.push(LT::ExclamationMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'~' => tokens.push(lex_and_consume(&mut chars, LT::Tilde)),
|
||||||
|
'@' => tokens.push(lex_and_consume(&mut chars, LT::AtSymbol)),
|
||||||
|
';' => tokens.push(lex_and_consume(&mut chars, LT::Semicolon)),
|
||||||
|
':' => tokens.push(lex_and_consume(&mut chars, LT::Colon)),
|
||||||
|
|
||||||
|
'(' => tokens.push(lex_and_consume(&mut chars, LT::OpenBracket)),
|
||||||
|
')' => tokens.push(lex_and_consume(&mut chars, LT::CloseBracket)),
|
||||||
|
'{' => tokens.push(lex_and_consume(&mut chars, LT::OpenCurlyBracket)),
|
||||||
|
'}' => tokens.push(lex_and_consume(&mut chars, LT::CloseCurlyBracket)),
|
||||||
|
'[' => tokens.push(lex_and_consume(&mut chars, LT::OpenBlockBracket)),
|
||||||
|
']' => tokens.push(lex_and_consume(&mut chars, LT::CloseBlockBracket)),
|
||||||
|
|
||||||
|
'0'..'9' => tokens.push(lex_numeric(&mut chars)),
|
||||||
|
'a'..'z' | 'A'..'Z' | '_' => tokens.push(lex_keyword_or_identifier(&mut chars)),
|
||||||
|
'\'' => tokens.push(lex_string(&mut chars, &'\'', false)),
|
||||||
|
'"' if chars.peek() == Some(&'\"') && chars.peek() == Some(&'\"') => {
|
||||||
|
tokens.push(lex_string(&mut chars, &'"', true))
|
||||||
|
}
|
||||||
|
'"' => tokens.push(lex_string(&mut chars, &'"', false)),
|
||||||
|
|
||||||
|
// TODO: Definitely not unreachable. Log a proper error here.
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
tokens.push(LT::EndOfFile);
|
||||||
|
reading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
use super::lex_tokens::LexToken;
|
||||||
|
use super::lexer::lex;
|
||||||
|
|
||||||
|
macro_rules! lex_token_test {
|
||||||
|
( $a: ident, $b: expr, $c: expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $a() {
|
||||||
|
let tokens = lex($b);
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
assert_eq!(tokens[0], $c);
|
||||||
|
assert_eq!(tokens[1], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lex_identifier_test {
|
||||||
|
( $a: ident, $b: expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $a() {
|
||||||
|
let tokens = lex($b);
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
assert_eq!(tokens[0], LexToken::Identifier($b.to_string()));
|
||||||
|
assert_eq!(tokens[1], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lex_integer_test {
|
||||||
|
( $a: ident, $b: expr, $c: expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $a() {
|
||||||
|
let tokens = lex($b);
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
assert_eq!(tokens[0], LexToken::IntegerLiteral($c));
|
||||||
|
assert_eq!(tokens[1], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lex_float_test {
|
||||||
|
( $a: ident, $b: expr, $c: expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $a() {
|
||||||
|
let tokens = lex($b);
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
assert_eq!(tokens[0], LexToken::FloatLiteral($c));
|
||||||
|
assert_eq!(tokens[1], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lex_string_test {
|
||||||
|
( $a: ident, $b: expr, $c: expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $a() {
|
||||||
|
let tokens = lex($b);
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
assert_eq!(tokens[0], LexToken::StringLiteral($c.to_string()));
|
||||||
|
assert_eq!(tokens[1], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lex_token_test!(lex_space, " ", LexToken::WhiteSpace);
|
||||||
|
lex_token_test!(lex_tab, "\t", LexToken::WhiteSpace);
|
||||||
|
lex_token_test!(lex_return_line, "\r", LexToken::WhiteSpace);
|
||||||
|
lex_token_test!(lex_newline, "\n", LexToken::WhiteSpace);
|
||||||
|
|
||||||
|
lex_token_test!(lex_equals, "=", LexToken::Equals);
|
||||||
|
lex_token_test!(lex_equals_equals, "==", LexToken::EqualsEquals);
|
||||||
|
lex_token_test!(lex_plus, "+", LexToken::Plus);
|
||||||
|
lex_token_test!(lex_plus_plus, "++", LexToken::PlusPlus);
|
||||||
|
lex_token_test!(lex_plus_equals, "+=", LexToken::PlusEquals);
|
||||||
|
lex_token_test!(lex_minus, "-", LexToken::Minus);
|
||||||
|
lex_token_test!(lex_minus_minus, "--", LexToken::MinusMinus);
|
||||||
|
lex_token_test!(lex_minus_equals, "-=", LexToken::MinusEquals);
|
||||||
|
lex_token_test!(lex_star, "*", LexToken::Star);
|
||||||
|
lex_token_test!(lex_star_equals, "*=", LexToken::StarEquals);
|
||||||
|
lex_token_test!(lex_star_star, "**", LexToken::StarStar);
|
||||||
|
lex_token_test!(lex_star_star_equals, "**=", LexToken::StarStarEquals);
|
||||||
|
lex_token_test!(lex_slash, "/", LexToken::Slash);
|
||||||
|
lex_token_test!(lex_slash_equals, "/=", LexToken::SlashEquals);
|
||||||
|
lex_token_test!(lex_percent, "%", LexToken::Percent);
|
||||||
|
lex_token_test!(lex_percent_equals, "%=", LexToken::PercentEquals);
|
||||||
|
|
||||||
|
lex_token_test!(lex_exclamation_mark, "!", LexToken::ExclamationMark);
|
||||||
|
lex_token_test!(lex_not_equals, "!=", LexToken::NotEquals);
|
||||||
|
lex_token_test!(lex_not_is_keyword, "!is", LexToken::NotIsKeyword);
|
||||||
|
|
||||||
|
lex_token_test!(lex_vert_line, "|", LexToken::VerticalLine);
|
||||||
|
lex_token_test!(lex_vert_line_equals, "|=", LexToken::LineEquals);
|
||||||
|
lex_token_test!(lex_line_line, "||", LexToken::LineLine);
|
||||||
|
|
||||||
|
lex_token_test!(lex_ampersand, "&", LexToken::Ampersand);
|
||||||
|
lex_token_test!(lex_ampersand_equals, "&=", LexToken::AmpersandEquals);
|
||||||
|
lex_token_test!(lex_ampersand_ampersand, "&&", LexToken::AmpersandAmpersand);
|
||||||
|
lex_token_test!(lex_less_than, "<", LexToken::LessThan);
|
||||||
|
lex_token_test!(lex_less_than_equals, "<=", LexToken::LessThanEquals);
|
||||||
|
lex_token_test!(lex_left_left, "<<", LexToken::LeftLeft);
|
||||||
|
lex_token_test!(lex_left_left_equals, "<<=", LexToken::LeftLeftEquals);
|
||||||
|
|
||||||
|
lex_token_test!(lex_greater_than, ">", LexToken::GreaterThan);
|
||||||
|
lex_token_test!(lex_greater_than_equals, ">=", LexToken::GreaterThanEquals);
|
||||||
|
lex_token_test!(lex_right_right, ">>", LexToken::RightRight);
|
||||||
|
lex_token_test!(lex_right_right_equals, ">>=", LexToken::RightRightEquals);
|
||||||
|
lex_token_test!(lex_right_right_right, ">>>", LexToken::RightRightRight);
|
||||||
|
lex_token_test!(
|
||||||
|
lex_right_right_right_equals,
|
||||||
|
">>>=",
|
||||||
|
LexToken::RightRightRightEquals
|
||||||
|
);
|
||||||
|
|
||||||
|
lex_token_test!(lex_tilde, "~", LexToken::Tilde);
|
||||||
|
lex_token_test!(lex_at_symbol, "@", LexToken::AtSymbol);
|
||||||
|
|
||||||
|
lex_token_test!(lex_and_keyword, "and", LexToken::AndKeyword);
|
||||||
|
lex_token_test!(lex_abstract_keyword, "abstract", LexToken::AbstractKeyword);
|
||||||
|
lex_token_test!(lex_auto_keyword, "auto", LexToken::AutoKeyword);
|
||||||
|
lex_token_test!(lex_bool_keyword, "bool", LexToken::BoolKeyword);
|
||||||
|
lex_token_test!(lex_break_keyword, "break", LexToken::BreakKeyword);
|
||||||
|
lex_token_test!(lex_case_keyword, "case", LexToken::CaseKeyword);
|
||||||
|
lex_token_test!(lex_cast_keyword, "cast", LexToken::CastKeyword);
|
||||||
|
lex_token_test!(lex_catch_keyword, "catch", LexToken::CatchKeyword);
|
||||||
|
lex_token_test!(lex_class_keyword, "class", LexToken::ClassKeyword);
|
||||||
|
lex_token_test!(lex_const_keyword, "const", LexToken::ConstKeyword);
|
||||||
|
lex_token_test!(lex_continue_keyword, "continue", LexToken::ContinueKeyword);
|
||||||
|
lex_token_test!(lex_default_keyword, "default", LexToken::DefaultKeyword);
|
||||||
|
lex_token_test!(lex_do_keyword, "do", LexToken::DoKeyword);
|
||||||
|
lex_token_test!(lex_double_keyword, "double", LexToken::DoubleKeyword);
|
||||||
|
lex_token_test!(lex_else_keyword, "else", LexToken::ElseKeyword);
|
||||||
|
lex_token_test!(lex_enum_keyword, "enum", LexToken::EnumKeyword);
|
||||||
|
lex_token_test!(lex_explicit_keyword, "explicit", LexToken::ExplicitKeyword);
|
||||||
|
lex_token_test!(lex_external_keyword, "external", LexToken::ExternalKeyword);
|
||||||
|
lex_token_test!(lex_false_keyword, "false", LexToken::FalseKeyword);
|
||||||
|
lex_token_test!(lex_final_keyword, "final", LexToken::FinalKeyword);
|
||||||
|
lex_token_test!(lex_float_keyword, "float", LexToken::FloatKeyword);
|
||||||
|
lex_token_test!(lex_for_keyword, "for", LexToken::ForKeyword);
|
||||||
|
lex_token_test!(lex_from_keyword, "from", LexToken::FromKeyword);
|
||||||
|
lex_token_test!(lex_funcdef_keyword, "funcdef", LexToken::FuncDefKeyword);
|
||||||
|
lex_token_test!(lex_function_keyword, "function", LexToken::FunctionKeyword);
|
||||||
|
lex_token_test!(lex_get_keyword, "get", LexToken::GetKeyword);
|
||||||
|
lex_token_test!(lex_if_keyword, "if", LexToken::IfKeyword);
|
||||||
|
lex_token_test!(lex_import_keyword, "import", LexToken::ImportKeyword);
|
||||||
|
lex_token_test!(lex_in_keyword, "in", LexToken::InKeyword);
|
||||||
|
lex_token_test!(lex_inout_keyword, "inout", LexToken::InOutKeyword);
|
||||||
|
lex_token_test!(lex_int_keyword, "int", LexToken::IntKeyword);
|
||||||
|
lex_token_test!(
|
||||||
|
lex_interface_keyword,
|
||||||
|
"interface",
|
||||||
|
LexToken::InterfaceKeyword
|
||||||
|
);
|
||||||
|
lex_token_test!(lex_int8_keyword, "int8", LexToken::Int8Keyword);
|
||||||
|
lex_token_test!(lex_int16_keyword, "int16", LexToken::Int16Keyword);
|
||||||
|
lex_token_test!(lex_int32_keyword, "int32", LexToken::Int32Keyword);
|
||||||
|
lex_token_test!(lex_int64_keyword, "int64", LexToken::Int64Keyword);
|
||||||
|
lex_token_test!(lex_is_keyword, "is", LexToken::IsKeyword);
|
||||||
|
lex_token_test!(lex_mixin_keyword, "mixin", LexToken::MixinKeyword);
|
||||||
|
lex_token_test!(
|
||||||
|
lex_namespace_keyword,
|
||||||
|
"namespace",
|
||||||
|
LexToken::NamespaceKeyword
|
||||||
|
);
|
||||||
|
lex_token_test!(lex_not_keyword, "not", LexToken::NotKeyword);
|
||||||
|
lex_token_test!(lex_null_keyword, "null", LexToken::NullKeyword);
|
||||||
|
lex_token_test!(lex_or_keyword, "or", LexToken::OrKeyword);
|
||||||
|
lex_token_test!(lex_out_keyword, "out", LexToken::OutKeyword);
|
||||||
|
lex_token_test!(lex_override_keyword, "override", LexToken::OverrideKeyword);
|
||||||
|
lex_token_test!(lex_private_keyword, "private", LexToken::PrivateKeyword);
|
||||||
|
lex_token_test!(lex_property_keyword, "property", LexToken::PropertyKeyword);
|
||||||
|
lex_token_test!(
|
||||||
|
lex_protected_keyword,
|
||||||
|
"protected",
|
||||||
|
LexToken::ProtectedKeyword
|
||||||
|
);
|
||||||
|
|
||||||
|
lex_token_test!(lex_return_keyword, "return", LexToken::ReturnKeyword);
|
||||||
|
lex_token_test!(lex_set_keyword, "set", LexToken::SetKeyword);
|
||||||
|
lex_token_test!(lex_shared_keyword, "shared", LexToken::SharedKeyword);
|
||||||
|
lex_token_test!(lex_super_keyword, "super", LexToken::SuperKeyword);
|
||||||
|
lex_token_test!(lex_switch_keyword, "switch", LexToken::SwitchKeyword);
|
||||||
|
lex_token_test!(lex_this_keyword, "this", LexToken::ThisKeyword);
|
||||||
|
lex_token_test!(lex_true_keyword, "true", LexToken::TrueKeyword);
|
||||||
|
lex_token_test!(lex_try_keyword, "try", LexToken::TryKeyword);
|
||||||
|
lex_token_test!(lex_typedef_keyword, "typedef", LexToken::TypeDefKeyword);
|
||||||
|
lex_token_test!(lex_uint_keyword, "uint", LexToken::UintKeyword);
|
||||||
|
lex_token_test!(lex_uint8_keyword, "uint8", LexToken::Uint8Keyword);
|
||||||
|
lex_token_test!(lex_uint16_keyword, "uint16", LexToken::Uint16Keyword);
|
||||||
|
lex_token_test!(lex_uint32_keyword, "uint32", LexToken::Uint32Keyword);
|
||||||
|
|
||||||
|
lex_token_test!(lex_void_keyword, "void", LexToken::VoidKeyword);
|
||||||
|
lex_token_test!(lex_while_keyword, "while", LexToken::WhileKeyword);
|
||||||
|
lex_token_test!(lex_xor_keyword, "xor", LexToken::XorKeyword);
|
||||||
|
|
||||||
|
lex_identifier_test!(lex_basic_identifier_foo, "foo");
|
||||||
|
lex_identifier_test!(lex_basic_identifier_foobar, "foobar");
|
||||||
|
|
||||||
|
lex_integer_test!(lex_zero, "0", 0);
|
||||||
|
lex_integer_test!(lex_one_two_three_four, "1234", 1234);
|
||||||
|
lex_integer_test!(lex_specific_one_two_three_four, "0d1234", 1234);
|
||||||
|
lex_integer_test!(lex_decimal_with_underline, "123_456", 123456);
|
||||||
|
lex_integer_test!(lex_specific_decimal_with_underline, "0D123_456", 123456);
|
||||||
|
lex_integer_test!(lex_hexadecimal_0f, "0X0F", 15);
|
||||||
|
lex_integer_test!(lex_hexadecimal_ff, "0xff", 255);
|
||||||
|
lex_integer_test!(lex_hexadecimal_ff_ff, "0xff_ff", 65535);
|
||||||
|
lex_integer_test!(lex_octal_112, "0o112", 74);
|
||||||
|
lex_integer_test!(lex_binary_1110, "0b1110", 14);
|
||||||
|
lex_integer_test!(lex_binary_01110, "0b01110", 14);
|
||||||
|
|
||||||
|
lex_float_test!(lex_zero_float, "0.0", 0.0);
|
||||||
|
lex_float_test!(lex_half, "0.5", 0.5);
|
||||||
|
lex_float_test!(lex_point_0_5, "0.05", 0.05);
|
||||||
|
lex_float_test!(lex_half_with_exponent, "0.5e10", 0.5e10);
|
||||||
|
|
||||||
|
lex_string_test!(lex_simple_string, "\"foo\"", "foo");
|
||||||
|
lex_string_test!(lex_simple_string_single_quote, "\'foo\'", "foo");
|
||||||
|
lex_string_test!(lex_string_with_escape, "\"fo\\\"o\"", "fo\"o");
|
||||||
|
lex_string_test!(lex_string_with_new_line, "\"fo\\no\"", "fo\no");
|
||||||
|
lex_string_test!(lex_heredoc_string, "\"\"\"foo\"\"\"", "foo");
|
||||||
|
lex_string_test!(lex_heredoc_string_with_quote, "\"\"\"fo\"o\"\"\"", "fo\"o");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lex_two_identifier() {
|
||||||
|
let tokens = lex("foo bar");
|
||||||
|
assert_eq!(tokens.len(), 4);
|
||||||
|
assert_eq!(tokens[0], LexToken::Identifier("foo".to_string()));
|
||||||
|
assert_eq!(tokens[1], LexToken::WhiteSpace);
|
||||||
|
assert_eq!(tokens[2], LexToken::Identifier("bar".to_string()));
|
||||||
|
assert_eq!(tokens[3], LexToken::EndOfFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lex_multiple_tokens_with_not_is() {
|
||||||
|
let tokens = lex("a !is b");
|
||||||
|
assert_eq!(tokens.len(), 6);
|
||||||
|
assert_eq!(tokens[0], LexToken::Identifier("a".to_string()));
|
||||||
|
assert_eq!(tokens[1], LexToken::WhiteSpace);
|
||||||
|
assert_eq!(tokens[2], LexToken::NotIsKeyword);
|
||||||
|
assert_eq!(tokens[3], LexToken::WhiteSpace);
|
||||||
|
assert_eq!(tokens[4], LexToken::Identifier("b".to_string()));
|
||||||
|
assert_eq!(tokens[5], LexToken::EndOfFile);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod lex_numerical;
|
||||||
|
pub mod lex_tokens;
|
||||||
|
pub mod lexer;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod lexer_tests;
|
Loading…
Reference in New Issue