save work before rewriting in rust

This commit is contained in:
Garen Tyler 2020-10-17 22:09:56 -06:00
parent 5062b3aebe
commit 7c8194633b
17 changed files with 576 additions and 384 deletions

5
Cargo.lock generated
View File

@ -1,5 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "pivot"
version = "0.1.0"

View File

@ -1,9 +0,0 @@
[package]
name = "pivot"
version = "0.1.0"
authors = ["ElementG9 <garentyler@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

0
errors.txt Normal file
View File

10
output.s Normal file
View File

@ -0,0 +1,10 @@
.global main
main:
push {fp, lr}
ldr r0, =1
cmp r0, #1
moveq r0, #'.'
movne r0, #'F'
bl putchar
mov r0, #0
pop {fp, pc}

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "pivot",
"version": "1.0.0",
"description": "A programming language",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"run": "deno run -c tsconfig.json --allow-read --allow-write --unstable src/index.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ElementG9/pivot.git"
},
"author": "Garen Tyler",
"license": "MIT",
"bugs": {
"url": "https://github.com/ElementG9/pivot/issues"
},
"homepage": "https://github.com/ElementG9/pivot#readme"
}

273
src/ast.ts Normal file
View File

@ -0,0 +1,273 @@
import { emit } from './codegen.ts';
export interface AST {
emit(): void;
equals(other: AST): boolean;
}
// Number node.
export class Num implements AST {
constructor(public value: number) {}
emit() {
emit(` ldr r0, =${this.value}`);
}
equals(other: AST): boolean {
return other instanceof Num
&& this.value === other.value;
}
}
// Identifier node.
export class Id implements AST {
constructor(public value: string) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof Id
&& this.value === other.value;
}
}
// Operator nodes.
export class Not implements AST {
constructor(public operand: AST) {}
emit() {
this.operand.emit();
emit(' cmp r0, #0');
emit(' moveq r0, #1');
emit(' movne r0, #0');
}
equals(other: AST): boolean {
return other instanceof Not
&& this.operand.equals(other.operand);
}
}
export abstract class Infix {
constructor(public left: AST, public right: AST) {}
emit() {
this.left.emit();
emit(' push {r0, ip}');
this.right.emit();
emit(' pop {r1, ip}');
}
}
export class Equal extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' cmp r0, r1');
emit(' moveq r0, #1');
emit(' movne r0, #0');
}
equals(other: AST): boolean {
return other instanceof Equal
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
export class NotEqual extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' cmp r0, r1');
emit(' moveq r0, #0');
emit(' movne r0, #1');
}
equals(other: AST): boolean {
return other instanceof NotEqual
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
export class Add extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' add r0, r0, r1');
}
equals(other: AST): boolean {
return other instanceof Add
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
export class Subtract extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' sub r0, r0, r1');
}
equals(other: AST): boolean {
return other instanceof Subtract
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
export class Multiply extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' mul r0, r0, r1');
}
equals(other: AST): boolean {
return other instanceof Multiply
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
export class Divide extends Infix implements AST {
constructor(public left: AST, public right: AST) {
super(left, right);
}
emit() {
super.emit();
emit(' udiv r0, r0, r1');
}
equals(other: AST): boolean {
return other instanceof Divide
&& this.left.equals(other.left)
&& this.right.equals(other.right);
}
}
// Function call node.
export class FunctionCall implements AST {
constructor(public callee: string, public args: Array<AST>) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof FunctionCall
&& this.callee === other.callee
&& this.args.length === other.args.length
&& this.args.every((arg: AST, i: number) => arg.equals(other.args[i]));
}
}
// Return node.
export class Return implements AST {
constructor(public operand: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof Return
&& this.operand.equals(other.operand);
}
}
// Block node.
export class Block implements AST {
constructor(public statements: Array<AST>) {}
emit() {
this.statements.forEach((statement: any) => statement.emit());
}
equals(other: AST): boolean {
return other instanceof Block
&& this.statements.length === other.statements.length
&& this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i]));
}
}
// If node.
export class If implements AST {
constructor(public conditional: AST,
public consequence: AST,
public alternative: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof If
&& this.conditional.equals(other.conditional)
&& this.consequence.equals(other.consequence)
&& this.alternative.equals(other.alternative);
}
}
// Function definition node.
export class FunctionDefinition implements AST {
constructor(public name: string,
public parameters: Array<string>,
public body: AST) {}
emit() {
throw Error(`Not implemented yet (${this.name})`);
}
equals(other: AST): boolean {
return other instanceof FunctionDefinition
&& this.name === other.name
&& this.parameters.length === other.parameters.length
&& this.parameters.every((arg: string, i: number) => arg === other.parameters[i])
&& this.body.equals(other.body);
}
}
// Variable declaration node.
export class VariableDeclaration implements AST {
constructor(public name: string, public value: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof VariableDeclaration
&& this.name === other.name
&& this.value.equals(other.value);
}
}
// Assignment node.
export class Assign implements AST {
constructor(public name: string, public value: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof Assign
&& this.name === other.name
&& this.value.equals(other.value);
}
}
// While loop node.
export class While implements AST {
constructor(public conditional: AST, public body: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof While
&& this.conditional.equals(other.conditional)
&& this.body.equals(other.body);
}
}
// Variable node.
export class Var implements AST {
constructor(public name: string, public value: AST) {}
emit() {
throw Error('Not implemented yet');
}
equals(other: AST): boolean {
return other instanceof Var
&& this.name === other.name
&& this.value.equals(other.value);
}
}

55
src/codegen.ts Normal file
View File

@ -0,0 +1,55 @@
import { AST } from './ast.ts';
import { existsSync } from 'https://deno.land/std/fs/mod.ts';
const outputFileName = '../output.s';
const outputToFile = false;
export async function emit(input: string) {
if (outputToFile) {
if (existsSync(outputFileName)) {
await Deno.remove(outputFileName);
}
await Deno.create(outputFileName);
const outputFile = await Deno.open(outputFileName, {
write: true,
append: true,
});
await Deno.write(outputFile.rid, new TextEncoder().encode(input));
} else {
console.log(input);
}
}
export class Main implements AST {
constructor(public statements: Array<AST>) {}
emit() {
emit('.global main');
emit('main:');
emit(' push {fp, lr}');
this.statements.forEach((statement: any) => statement.emit());
emit(' mov r0, #0');
emit(' pop {fp, pc}');
}
equals(other: AST): boolean {
return other instanceof Main
&& this.statements.length === other.statements.length
&& this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i]));
}
}
export class Assert implements AST {
constructor(public condition: AST) {}
emit() {
this.condition.emit();
emit(' cmp r0, #1');
emit(` moveq r0, #'.'`);
emit(` movne r0, #'F'`);
emit(' bl putchar');
}
equals(other: AST): boolean {
return other instanceof Assert
&& this.condition.equals(other.condition);
}
}

10
src/index.ts Normal file
View File

@ -0,0 +1,10 @@
import { parse } from './parser.ts';
let result = parse(`
function main() {
assert(1);
assert(!0);
}
`);
// console.log(JSON.stringify(result, null, 2));
result.emit();

View File

@ -1,42 +0,0 @@
use super::parser::*;
pub fn interpret(ast: Program) {
for stmt in ast {
use Statement::*;
match stmt {
FunctionCall { name, arguments } => {
let name: &str = &name;
// println!("{} called!", name);
match name {
"log" => {
let mut args: Vec<String> = vec![];
for a in arguments {
match a {
Expression::Literal(literal) => match literal {
Literal::StringLiteral(s) => args.push(s),
Literal::IntLiteral(i) => args.push(format!("{}i", i)),
Literal::FloatLiteral(f) => {
if f.fract() == 0.0 {
args.push(format!("{}.0f", f));
} else {
args.push(format!("{}f", f));
}
}
Literal::BooleanLiteral(b) => args.push(format!("{}", b)),
},
Expression::Null => {
args.push("null".to_owned());
}
}
}
println!("{}", args.join(", "));
}
_ => {}
}
}
Nop => {
// println!("No-op!");
}
}
}
}

View File

@ -1,15 +0,0 @@
pub mod interpreter;
pub mod parser;
pub mod tokenizer;
pub fn interpret(source: &str) {
let tokens = tokenizer::tokenize(source);
// println!("{:#?}", tokens);
let ast = parser::parse(tokens);
// println!("{:#?}", ast);
interpreter::interpret(ast);
}
pub fn interpret_file(filename: &str) {
let src = std::fs::read_to_string(filename).unwrap();
interpret(&src);
}

0
src/lib.ts Normal file
View File

View File

@ -1,8 +0,0 @@
fn main() {
let args = std::env::args().collect::<Vec<String>>();
if args.len() < 2 {
println!("Usage: pivot <filename>");
} else {
pivot::interpret_file(&args[1]);
}
}

View File

@ -1,127 +0,0 @@
use super::tokenizer::{Token, TokenKind};
pub fn parse(mut tokens: Vec<Token>) -> Program {
let mut stmts: Vec<Vec<Token>> = vec![];
let mut current = 0;
loop {
if current >= tokens.len() {
break;
}
// print!("{:?}", tokens[current]);
if tokens[current].kind == TokenKind::Semicolon {
stmts.push(tokens.drain(..=current).collect::<Vec<Token>>());
current = 0;
} else {
current += 1;
}
}
let mut statements = vec![];
// for s in &stmts {
// for t in s {
// print!("{:?}", t.kind);
// }
// print!("\n");
// }
for s in stmts {
statements.push(parse_statement(s));
}
statements
}
fn parse_statement(statement: Vec<Token>) -> Statement {
if statement.len() == 1 {
// Must just be a semicolon.
return Statement::Nop;
}
let parse_function_call = |tokens: &Vec<Token>| -> Option<Statement> {
// Check for <identifier>( ... );
if tokens[0].kind != TokenKind::Identifier {
return None;
} else if tokens[1].kind != TokenKind::LeftParen {
return None;
} else if tokens[tokens.len() - 2].kind != TokenKind::RightParen {
return None;
} else if tokens[tokens.len() - 1].kind != TokenKind::Semicolon {
return None;
} else {
let function_name = tokens[0].value.clone();
let mut args = vec![];
let mut current = 2;
loop {
args.push(parse_expression(tokens, &mut current));
if tokens[current].kind == TokenKind::Comma {
current += 1;
}
if tokens[current].kind == TokenKind::RightParen {
break;
}
}
Some(Statement::FunctionCall {
name: function_name,
arguments: args,
})
}
};
// The only form of statement.
parse_function_call(&statement).expect("could not parse function call")
}
fn parse_expression(tokens: &Vec<Token>, current: &mut usize) -> Expression {
if tokens[*current].kind == TokenKind::StringLiteral {
let out = Expression::Literal(Literal::StringLiteral(tokens[*current].value.clone()));
*current += 1;
out
} else if tokens[*current].kind == TokenKind::IntLiteral {
let val = tokens[*current]
.value
.clone()
.parse::<i32>()
.expect("could not parse int literal");
let out = Expression::Literal(Literal::IntLiteral(val));
*current += 1;
out
} else if tokens[*current].kind == TokenKind::FloatLiteral {
let val = tokens[*current]
.value
.clone()
.parse::<f32>()
.expect("could not parse float literal");
let out = Expression::Literal(Literal::FloatLiteral(val));
*current += 1;
out
} else if tokens[*current].kind == TokenKind::BooleanLiteral {
let mut val = false;
if tokens[*current].value == "true" {
val = true;
} else if tokens[*current].value == "false" {
val = false;
}
*current += 1;
Expression::Literal(Literal::BooleanLiteral(val))
} else {
Expression::Null
}
}
pub type Program = Vec<Statement>;
#[derive(Debug, PartialEq)]
pub enum Statement {
FunctionCall {
name: String,
arguments: Vec<Expression>,
},
Nop, // Equivalent to a C nop statement.
}
#[derive(Debug, PartialEq)]
pub enum Expression {
Literal(Literal),
Null,
}
#[derive(Debug, PartialEq)]
pub enum Literal {
StringLiteral(String),
IntLiteral(i32),
FloatLiteral(f32),
BooleanLiteral(bool),
}

200
src/parser.ts Normal file
View File

@ -0,0 +1,200 @@
import { AST, Num, Id, Not, Equal, NotEqual, Add, Subtract, Multiply, Divide, FunctionCall, Return, If, While, Var, Assign, Block, FunctionDefinition } from './ast.ts';
import { Main, Assert } from './codegen.ts';
export function parse(source: string): AST {
// Whitespace and comments.
let whitespace = Parser.regex(/[ \n\r\t]+/y);
let comments = Parser.regex(/[/][/].*/y).or(
Parser.regex(/[/][*].*[*][/]/sy)
);
let ignored = Parser.zeroOrMore(whitespace.or(comments));
// Tokens
let token = (pattern: RegExp) => Parser.regex(pattern).bind((value: any) => ignored.and(Parser.constant(value)));
let FUNCTION = token(/function\b/y);
let IF = token(/if\b/y);
let ELSE = token(/else\b/y);
let RETURN = token(/return\b/y);
let ASSIGN = token(/=/y).map(_ => Assign);
let VAR = token(/var\b/y);
let WHILE = token(/while\b/y);
let COMMA = token(/[,]/y);
let SEMICOLON = token(/[;]/y);
let LEFT_PAREN = token(/[(]/y);
let RIGHT_PAREN = token(/[)]/y);
let LEFT_BRACE = token(/[{]/y);
let RIGHT_BRACE = token(/[}]/y);
let NUMBER = token(/[0-9]/y).map((digits: any) => new Num(parseInt(digits)));
let ID = token(/[a-zA-Z_][a-zA-Z0-9_]*/y).map((x: any) => new Id(x));
let NOT = token(/!/y).map(_ => Not);
let EQUAL = token(/==/y).map(_ => Equal);
let NOT_EQUAL = token(/!=/y).map(_ => NotEqual);
let PLUS = token(/[+]/y).map(_ => Add);
let MINUS = token(/[-]/y).map(_ => Subtract);
let STAR = token(/[*]/y).map(_ => Multiply);
let SLASH = token(/[/]/y).map(_ => Divide);
// Expression parser
let expression: Parser<AST> = Parser.error('expression parser used before definition');
// Call parser
let args: Parser<Array<AST>> = expression.bind((arg: any) => Parser.zeroOrMore(COMMA.and(expression)).bind((args: any) => Parser.constant([arg, ...args]))).or(Parser.constant([]));
let functionCall: Parser<AST> = ID.bind((callee: any) => LEFT_PAREN.and(args.bind((args: any) => RIGHT_PAREN.and(Parser.constant(callee.equals(new Id('assert')) ? new Assert(args[0]) : new FunctionCall(callee, args))))));
// Atom
let atom: Parser<AST> = functionCall.or(ID).or(NUMBER).or(LEFT_PAREN.and(expression).bind((e: any) => RIGHT_PAREN.and(Parser.constant(e))));
// Unary operators
let unary: Parser<AST> = Parser.optional(NOT).bind((not: any) => atom.map((operand: any) => not ? new Not(operand) : operand));
// Infix operators
let infix = (operatorParser: any, operandParser: any) =>
operandParser.bind((operand: any) =>
Parser.zeroOrMore(
operatorParser.bind((operator: any) =>
operandParser.bind((operand: any) =>
Parser.constant({ operator, operand })
)
)
).map((operatorTerms: any) =>
operatorTerms.reduce((left: any, { operator, operand }: { operator: any, operand: any }) =>
new operator(left, operand), operand)
)
);
let product = infix(STAR.or(SLASH), unary);
let sum = infix(PLUS.or(MINUS), product);
let comparison = infix(EQUAL.or(NOT_EQUAL), sum);
// Associativity
// Closing the loop: expression
expression.parse = comparison.parse;
// Statement
let statement: Parser<AST> = Parser.error('statement parser used before definition');
let returnStatement: Parser<AST> = RETURN.and(expression).bind((operand: any) => SEMICOLON.and(Parser.constant(new Return(operand))));
let expressionStatement: Parser<AST> = expression.bind((operand: any) => SEMICOLON.and(Parser.constant(operand)));
let ifStatement: Parser<AST> = IF.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
RIGHT_PAREN.and(statement).bind((consequence: any) =>
ELSE.and(statement).bind((alternative: any) =>
Parser.constant(new If(conditional, consequence, alternative))
)
)
);
let whileStatement: Parser<AST> = WHILE.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
RIGHT_PAREN.and(statement).bind((body: any) =>
Parser.constant(new While(conditional, body))
)
);
let varStatement: Parser<AST> = VAR.and(ID).bind((name: any) =>
ASSIGN.and(expression).bind((value: any) =>
SEMICOLON.and(Parser.constant(new Var(name, value)))
)
);
let assignmentStatement: Parser<AST> = ID.bind((name: any) =>
ASSIGN.and(expression).bind((value: any) =>
SEMICOLON.and(Parser.constant(new Assign(name, value)))
)
);
let blockStatement: Parser<AST> = LEFT_BRACE.and(Parser.zeroOrMore(statement)).bind((statements: any) =>
RIGHT_BRACE.and(Parser.constant(new Block(statements)))
);
let parameters: Parser<Array<string>> = ID.bind((param: any) =>
Parser.zeroOrMore(COMMA.and(ID)).bind((params: any) =>
Parser.constant([param, ...params])
)
).or(Parser.constant([]));
let functionStatement: Parser<AST> = FUNCTION.and(ID).bind((name: any) =>
LEFT_PAREN.and(parameters).bind((parameters: any) =>
RIGHT_PAREN.and(blockStatement).bind((block: any) =>
Parser.constant(name.equals(new Id('main')) ? new Main(block.statements) : new FunctionDefinition(name, parameters, block))
)
)
);
let statementParser: Parser<AST> =
returnStatement
.or(functionStatement)
.or(ifStatement)
.or(whileStatement)
.or(varStatement)
.or(assignmentStatement)
.or(blockStatement)
.or(expressionStatement);
statement.parse = statementParser.parse;
let parser: Parser<AST> = ignored.and(Parser.zeroOrMore(statement)).map((statements: any) => new Block(statements));
return parser.parseStringToCompletion(source);
}
export class Parser<T> {
constructor(public parse: (src: Source) => (ParseResult<T> | null)) {}
parseStringToCompletion(string: string): T {
let source = new Source(string, 0);
let result = this.parse(source);
if (!result)
throw Error('Parse error at index 0');
let index = result.source.index;
if (index != result.source.string.length)
throw Error(`Parse error at index ${index}`);
return result.value;
}
static regex(regex: RegExp): Parser<string> {
return new Parser(source => source.match(regex));
}
static constant<U>(value: U): Parser<U> {
return new Parser(source => new ParseResult(value, source));
}
static error<U>(message: string): Parser<U> {
return new Parser(source => {
throw Error(message);
})
}
static zeroOrMore<U>(parser: Parser<U>): Parser<Array<U>> {
return new Parser(source => {
let results = [];
let item: any;
while (item = parser.parse(source)) {
source = item.source;
results.push(item.value);
}
return new ParseResult(results, source);
});
}
static optional<U>(parser: Parser<U>): Parser<U | null> {
return parser.or(Parser.constant(null));
}
or(parser: Parser<T>): Parser<T> {
return new Parser(source => {
let result = this.parse(source);
return result ? result : parser.parse(source);
});
}
bind<U>(callback: (value: T) => Parser<U>): Parser<U> {
return new Parser(source => {
let result = this.parse(source);
if (result) {
let value = result.value;
let source = result.source;
return callback(value).parse(source);
} else return null;
});
}
and<U>(parser: Parser<U>): Parser<U> {
return this.bind(_ => parser);
}
map<U>(callback: (t: T) => U): Parser<U> {
return this.bind(value => Parser.constant(callback(value)))
}
}
export class Source {
constructor(public string: string, public index: number) {}
match(regex: RegExp): (ParseResult<string> | null) {
regex.lastIndex = this.index;
let match = this.string.match(regex);
if (match) {
let value = match[0];
let newIndex = this.index + value.length;
let source = new Source(this.string, newIndex);
return new ParseResult(value, source);
}
return null;
}
}
export class ParseResult<T> {
constructor(public value: T, public source: Source) {}
}

View File

@ -1,159 +0,0 @@
#[derive(Debug, PartialEq)]
pub enum TokenKind {
Identifier,
StringLiteral,
IntLiteral,
FloatLiteral,
BooleanLiteral,
LeftParen,
RightParen,
Semicolon,
Comma,
}
#[derive(Debug, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub value: String,
pub index: usize,
}
pub fn tokenize(source: &str) -> Vec<Token> {
let chars: Vec<char> = source.chars().collect();
let mut tokens = Vec::new();
let mut current: usize = 0;
while let Some(c) = chars.get(current) {
if c.is_alphabetic() {
read_identifier(&chars, &mut current, &mut tokens);
} else if c.is_digit(10) {
read_number(&chars, &mut current, &mut tokens);
} else {
match c {
'\'' | '"' => read_string(&chars, &mut current, &mut tokens, c),
'(' => {
tokens.push(Token {
kind: TokenKind::LeftParen,
value: "(".to_owned(),
index: current,
});
current += 1;
}
')' => {
tokens.push(Token {
kind: TokenKind::RightParen,
value: ")".to_owned(),
index: current,
});
current += 1;
}
';' => {
tokens.push(Token {
kind: TokenKind::Semicolon,
value: ";".to_owned(),
index: current,
});
current += 1;
}
',' => {
tokens.push(Token {
kind: TokenKind::Comma,
value: ",".to_owned(),
index: current,
});
current += 1;
}
'/' => {
if chars.get(current + 1) == Some(&'/') {
// A "// ..." comment.
'comment: loop {
if chars.get(current) == Some(&'\n') || chars.get(current) == None {
break 'comment;
}
current += 1;
}
} else {
current += 1;
}
}
_ => current += 1, // Just skip it if it's incorrect.
}
}
}
tokens
}
fn read_identifier(chars: &Vec<char>, current: &mut usize, tokens: &mut Vec<Token>) {
let original_current = *current;
let mut identifier = String::new();
while let Some(c) = chars.get(*current) {
if c.is_alphabetic() {
identifier.push(*c);
*current += 1;
} else {
break;
}
}
let mut kind = TokenKind::Identifier;
if identifier == "true" || identifier == "false" {
kind = TokenKind::BooleanLiteral;
}
tokens.push(Token {
kind: kind,
value: identifier,
index: original_current,
});
}
fn read_string(chars: &Vec<char>, current: &mut usize, tokens: &mut Vec<Token>, delimiter: &char) {
let original_current = *current;
let mut string = String::new();
*current += 1; // Move forward from the first delimiter.
while let Some(c) = chars.get(*current) {
if c == delimiter {
*current += 1;
break;
} else {
string.push(*c);
*current += 1;
}
}
tokens.push(Token {
kind: TokenKind::StringLiteral,
value: string,
index: original_current,
});
}
fn read_number(chars: &Vec<char>, current: &mut usize, tokens: &mut Vec<Token>) {
let original_current = *current;
let mut kind = TokenKind::IntLiteral;
let mut num = String::new();
while let Some(c) = chars.get(*current) {
if c.is_digit(10) {
num.push(*c);
*current += 1;
} else if let Some(n) = chars.get(*current + 1) {
if *c == 'f' {
kind = TokenKind::FloatLiteral;
*current += 1;
break;
} else if *c == 'i' {
kind = TokenKind::IntLiteral;
*current += 1;
break;
} else if *c == '.' && n.is_digit(10) {
num.push(*c);
num.push(*n);
kind = TokenKind::FloatLiteral;
*current += 2;
} else {
break;
}
} else {
break;
}
}
tokens.push(Token {
kind: kind,
value: num,
index: original_current,
});
}

View File

@ -1,19 +1 @@
// A comment
// An implicit int.
log(2);
// An explicit int.
log(5i);
// An implicit float.
log(2.5);
// An explicit float.
log(3f);
// Booleans and multiple arguments.
log(true, false);
// Strings.
log("Hello world!");
// A string showing how different delimiters layer.
log("What's that over there?");
let x = {2 + 2};

7
tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"strictNullChecks": false
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}