✨ string literal.
parent
efdbafb645
commit
a0b8cda00d
|
@ -1,4 +1,8 @@
|
||||||
MODULE ::= INSTR*
|
MODULE ::= INSTR*
|
||||||
INSTR ::= EXPR semicolon
|
INSTR ::= EXPR semicolon
|
||||||
EXPR ::= BUILTIN
|
EXPR ::= BUILTIN
|
||||||
BUILTIN ::= bool | float | int
|
BUILTIN ::=
|
||||||
|
| bool
|
||||||
|
| float
|
||||||
|
| int
|
||||||
|
| string
|
||||||
|
|
|
@ -53,6 +53,55 @@ impl Lexer {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scan_string(&mut self) -> Option<Token> {
|
||||||
|
let mut value : String;
|
||||||
|
let mut length = 1;
|
||||||
|
|
||||||
|
if let Some('"') = self.text.chars().nth(self.cursor) {
|
||||||
|
value = String::from("");
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut escape = false;
|
||||||
|
let mut closed = false;
|
||||||
|
|
||||||
|
for c in self.text.chars().skip(length + self.cursor) {
|
||||||
|
if !escape && c == '\\' {
|
||||||
|
escape = true;
|
||||||
|
length += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if escape {
|
||||||
|
match c {
|
||||||
|
'"' => value.push('"'),
|
||||||
|
'\\' => value.push('\\'),
|
||||||
|
'n' => value.push('\n'),
|
||||||
|
't' => value.push('\t'),
|
||||||
|
'r' => value.push('\r'),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else if c == '"' {
|
||||||
|
closed = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
value.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
escape = false;
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if length >= 2 && closed {
|
||||||
|
return Some(Token {
|
||||||
|
position: 1 + self.cursor + length,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn scan_int(&self) -> Option<Token> {
|
fn scan_int(&self) -> Option<Token> {
|
||||||
let mut value = String::from("");
|
let mut value = String::from("");
|
||||||
|
@ -205,6 +254,11 @@ impl Iterator for Lexer {
|
||||||
return Some(Node::Semicolon);
|
return Some(Node::Semicolon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(token) = self.scan_string() {
|
||||||
|
self.cursor = token.position;
|
||||||
|
return Some(Node::String(token.value.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(token) = self.scan_float() {
|
if let Some(token) = self.scan_float() {
|
||||||
if let Ok(val) = token.value.parse::<f64>() {
|
if let Ok(val) = token.value.parse::<f64>() {
|
||||||
self.cursor = token.position;
|
self.cursor = token.position;
|
||||||
|
@ -320,4 +374,18 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string() -> Result<(), error::AstError> {
|
||||||
|
let mut lex = Lexer::new();
|
||||||
|
|
||||||
|
let res = lexer_run(&mut lex,
|
||||||
|
" \"s\\ta\\\"lut\\n\" ")?;
|
||||||
|
|
||||||
|
assert_eq!(1, res.len());
|
||||||
|
assert_eq!(Node::String(String::from("s\ta\"lut\n")),
|
||||||
|
*res.get(0).unwrap());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub enum Node {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
|
String(String),
|
||||||
Semicolon
|
Semicolon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,9 @@ impl Parser {
|
||||||
Some(Node::Bool(value))
|
Some(Node::Bool(value))
|
||||||
=> Ok(Some(Node::Bool(value))),
|
=> Ok(Some(Node::Bool(value))),
|
||||||
|
|
||||||
|
Some(Node::String(value))
|
||||||
|
=> Ok(Some(Node::String(value))),
|
||||||
|
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
Err(error::AstError {
|
Err(error::AstError {
|
||||||
msg: String::from(format!("unexpected {:?}", other)),
|
msg: String::from(format!("unexpected {:?}", other)),
|
||||||
|
@ -164,4 +167,16 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strings() -> TestResult {
|
||||||
|
test_parser(
|
||||||
|
Node::Module("mod".to_owned(), vec![
|
||||||
|
Node::String(String::from("pizza! "))
|
||||||
|
]),
|
||||||
|
" \"pizza! \"; "
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ impl Evaluator {
|
||||||
Node::Bool(value) => Ok(Some(Value::Bool(*value))),
|
Node::Bool(value) => Ok(Some(Value::Bool(*value))),
|
||||||
Node::Int(value) => Ok(Some(Value::Int(*value))),
|
Node::Int(value) => Ok(Some(Value::Int(*value))),
|
||||||
Node::Float(value) => Ok(Some(Value::Float(*value))),
|
Node::Float(value) => Ok(Some(Value::Float(*value))),
|
||||||
|
Node::String(value) => Ok(Some(Value::String(value.clone()))),
|
||||||
|
|
||||||
_ => Ok(None)
|
_ => Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -94,4 +95,12 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strings() -> TestResult {
|
||||||
|
test_eval_value(Value::String(String::from(" pizzza!!!")),
|
||||||
|
" \" pizzza!!!\"; ")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f64)
|
Float(f64),
|
||||||
|
String(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue