Commit 76ba9686 authored by insert's avatar insert

Initial commit

parents
.vscode/
dist/
node_modules/
nice.txt
\ No newline at end of file
function isAlphaNumeric(str) {
var code, i, len;
for (i = 0, len = str.length; i < len; i++) {
code = str.charCodeAt(i);
if (!(code > 47 && code < 58) && // numeric (0-9)
!(code > 64 && code < 91) && // upper alpha (A-Z)
!(code > 96 && code < 123)) { // lower alpha (a-z)
return false;
}
}
return true;
};
let status = {
NONE: 0,
ALPHANUMERIC: 1,
STRING: 2
};
function tokenise(source) {
let tokens = [];
let cur = -1;
let stat = status.NONE;
for (let i = 0; i < source.length; i++) {
let l = source[i];
if (stat != status.STRING && isAlphaNumeric(l)) {
if (stat == status.NONE) {
tokens.push(l);
cur++;
stat = status.ALPHANUMERIC;
} else {
tokens[cur] += l;
}
} else if (l == ' ') {
stat = status.NONE;
} else if (l == '\\') {
escapeNext = true;
} else if (["'", '"'].indexOf(l) > -1) {
stat = status.STRING;
tokens.push('');
cur++;
} else if (stat == status.STRING) {
tokens[cur] += l;
} else {
tokens.push(l);
cur++;
}
}
return tokens;
}
let source = `func sans { print 'same'; } print 'yourgonnahaveabadtime'; sans;`;
//let source = `func say text { print text; } func sans { say 'test'; say 'epic'; } say 'yeet'; sans;`;
let tokens = tokenise(source);
let types = {
NONE: 0,
FUNC: 1,
CALL: 2
};
function run(tokens, globals = {}) {
let variables = Object.assign({
print: {
type: 'function',
params: ['text'],
native: (text) => {
console.log(text);
}
}
}, globals);
let i = -1;
function getNextToken() {
i++;
if (!tokens[i]) throw new Error("Unexpected end of file!");
return tokens[i];
}
function layer() {
while (i + 1 < tokens.length) {
let token = getNextToken();
switch (token) {
case 'func':
let name = getNextToken();
let params = [];
while (params.indexOf('{') == -1) {
params.push(getNextToken());
}
params.pop();
let body = [];
while (body.indexOf(';') == -1) {
body.push(getNextToken());
}
variables[name] = {
type: 'function',
params,
body
};
break;
default:
let v = variables[token];
if (v && v.type == 'function') {
let args = [];
while (args.indexOf(';') == -1) {
args.push(getNextToken());
}
args.pop();
if (v.native) {
v.native.apply(this, args);
} else {
let obj = {};
v.params.forEach((key, i) => obj[key] = args[i]);
run(v.body, obj);
}
}
break;
}
}
}
layer();
}
run(tokens);
\ No newline at end of file
{
"name": "lang",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "10.12.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.24.tgz",
"integrity": "sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ=="
}
}
}
{
"name": "lang",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"spl": "dist/cli.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^10.12.24"
}
}
func square a {
return a * a;
}
n = 4;
r = square(n) 2 * n;
print 'n squared + 2n is' r;
fs.write 'nice.txt' 'now this is epic, 3 squared is: ' 9;
read = fs.read('nice.txt');
print read;
exists = fs.exists('nice.txt');
if exists {
print 'file exists!';
}
if n == 2 {
print 'whoa it is 2';
} elif n == 3 {
print '3 time';
} else {
print 'no';
}
\ No newline at end of file
import { Token } from "./Lexicons";
export enum Error {
TOKEN,
EOF,
UNEXPECTED,
NOT_DEFINED,
UNKNOWN_OPERATION,
UNKNOWN_VARIABLE_OPERATION
}
export function Throw(error: Error, token: Token) {
switch(error) {
case Error.TOKEN:
console.error('# Found an unexpected token.');
break;
case Error.EOF:
console.error('# Unexpected end of file!');
break;
case Error.UNEXPECTED:
console.error('# Unexpected symbol!');
break;
case Error.NOT_DEFINED:
console.error('# Variable not defined!');
break;
case Error.UNKNOWN_OPERATION:
console.error('# Unknown operation!');
break;
case Error.UNKNOWN_VARIABLE_OPERATION:
console.error('# Unknown var operation!');
break;
}
if (token)
console.error('# Where ' + token.raw + ' (' + token.line + ':' + token.column + ')');
else
console.error('# Where ???');
process.exit(0);
}
\ No newline at end of file
import Lexicon, { Lexicate, Token, PeekedToken } from "./Lexicons";
import CreateStore, { Store, Type, Variable } from "./Variable";
import { Throw, Error } from "./Error";
export function Stack(tokens: Token[], override?: Store, assign: boolean = false): any {
let variables = CreateStore(override);
let retValue;
let i = -1;
function getToken() {
i++;
let token = tokens[i];
if (!token) Throw(Error.EOF, {
lexicon: Lexicon.UNKNOWN,
line: 0, column: i, raw: 'EOF'
});
return token;
}
function peekToken() {
let token = <PeekedToken> tokens[i + 1];
if (!token) token = <any> {
type: Type.UNKNOWN
};
token.confirm = () => { i ++; };
return token;
}
function readBody() {
let body: Token[] = [];
function start() {
let i = 0;
body.forEach(x => {
if (x.lexicon === Lexicon.BODY_BEGIN)
i++;
});
return i;
}
function end() {
let i = 0;
body.forEach(x => {
if (x.lexicon === Lexicon.BODY_END)
i++;
});
return i;
}
while (end() < start() + 1) {
let token = getToken();
body.push(token);
}
body.pop();
return body;
}
function readTokens(until: Lexicon) {
let tokens: Token[] = [];
while (true) {
let token = getToken();
if (token.lexicon === until) break;
tokens.push(token);
}
return tokens;
}
while (i + 1 < tokens.length && !retValue) {
let token = getToken();
switch (token.lexicon) {
case Lexicon.ALPHANUMERIC:
switch (token.body) {
case 'func':
{
let nt = getToken();
if (nt.lexicon !== Lexicon.ALPHANUMERIC) Throw(Error.UNEXPECTED, token);
let name = <string> nt.body;
let params: string[] = [];
while (params.indexOf('{') < 0) {
let token = getToken();
if (token.lexicon === Lexicon.BODY_BEGIN) break;
if (token.lexicon !== Lexicon.ALPHANUMERIC) Throw(Error.UNEXPECTED, token);
params.push(<string> token.body);
}
let body = readBody();
if (variables[name]) Throw(Error.NOT_DEFINED, token);
variables[name] = {
type: Type.FUNCTION,
params,
tokens: body
};
}
break;
case 'if':
case 'elif':
{
let condition = readTokens(Lexicon.BODY_BEGIN);
let body = readBody();
if (Stack(condition, variables, true)) {
Stack(body, variables);
let peeked = peekToken();
while (peeked.lexicon === Lexicon.ALPHANUMERIC &&
(peeked.body === 'elif' || peeked.body === 'else')) {
peeked.confirm();
readTokens(Lexicon.BODY_BEGIN);
readBody();
peeked = peekToken();
}
}
}
break;
case 'else':
getToken();
Stack(readBody(), variables);
break;
case 'return':
let pass = readTokens(Lexicon.TERMINATOR);
if (pass.length > 0)
return Stack(pass, variables, true);
else
return;
default:
let peeked = peekToken();
let struct: string[] = [];
while (peeked.lexicon === Lexicon.PERIOD) {
peeked.confirm();
peeked = peekToken();
if (peeked.lexicon !== Lexicon.ALPHANUMERIC) Throw(Error.UNEXPECTED, peeked);
struct.push(<string> peeked.body);
peeked.confirm();
peeked = peekToken();
}
let v = variables[<string> token.body];
struct.forEach(key => {
if (v.children)
v = v.children[key];
else
Throw(Error.NOT_DEFINED, token);
});
if (v && peeked.lexicon !== Lexicon.ASSIGNMENT) {
if (v.type === Type.FUNCTION) {
let args: string[] = [];
if (peeked.lexicon === Lexicon.ARGUMENT_BEGIN) {
// ? LOADING ARGUMENTS
peeked.confirm();
let pass: Token[][] = [[]];
while (true) {
let token = getToken();
if (token.lexicon === Lexicon.ARGUMENT_END) break;
if (token.lexicon === Lexicon.DELIMETER) {
pass.push([]);
continue;
}
pass[pass.length - 1].push(token);
}
if (pass.length > 0 || pass[0].length > 0) {
pass.forEach(pass => {
args.push(Stack(pass, variables, true));
});
}
} else {
// ? NORMAL FN
while (true) {
let token = getToken();
if (token.lexicon === Lexicon.TERMINATOR) break;
if (!token.body) Throw(Error.UNEXPECTED, token);
if (token.lexicon === Lexicon.ALPHANUMERIC) {
let v = variables[<string> token.body];
if (v) {
if (v.type === Type.FUNCTION) {
args.push(`[Function ${token.body}]`);
} else {
args.push(v.value);
}
continue;
} else {
Throw(Error.NOT_DEFINED, token);
}
}
args.push(<string> token.body);
}
}
let ret;
if (v.tokens) {
let ovr: {
[key: string]: Variable
} = Object.assign({}, variables);
(<string[]> v.params).forEach((k, i) => {
ovr[k] = {
type: Type.VALUE,
value: args[i]
};
});
ret = Stack(v.tokens, ovr);
} else if (v.native) {
ret = v.native.apply(v, args);
}
retValue = ret;
} else {
retValue = v.value;
}
} else {
if (peeked.lexicon === Lexicon.ASSIGNMENT) {
peeked.confirm();
let tokens = readTokens(Lexicon.TERMINATOR);
if (!v) {
v = {
type: Type.VALUE
};
}
v.value = Stack(tokens, variables, true);
variables[<string> token.body] = v;
} else {
Throw(Error.UNKNOWN_VARIABLE_OPERATION, token);
}
}
}
break;
case Lexicon.NUMERIC:
case Lexicon.STRING:
retValue = token.body;
break;
default:
Throw(Error.UNKNOWN_OPERATION, token);
}
}
if (assign) {
let peeked = peekToken();
if (peeked.lexicon === Lexicon.OPERATOR_ADDITION ||
peeked.lexicon === Lexicon.OPERATOR_MINUS ||
peeked.lexicon === Lexicon.OPERATOR_MULTIPLICATION ||
peeked.lexicon === Lexicon.OPERATOR_DIVISION) {
peeked.confirm();
let toev: Token[] = [];
while (i + 1 < tokens.length) {
let token = getToken();
if (token.lexicon === Lexicon.TERMINATOR) break;
toev.push(token);
}
let operand = Stack(toev, variables, true);
if (peeked.lexicon === Lexicon.OPERATOR_ADDITION)
retValue += operand;
else if (peeked.lexicon === Lexicon.OPERATOR_MINUS)
retValue -= operand;
else if (peeked.lexicon === Lexicon.OPERATOR_MULTIPLICATION)
retValue *= operand;
else if (peeked.lexicon === Lexicon.OPERATOR_DIVISION)
retValue /= operand;
} else if (peeked.lexicon === Lexicon.STRING
|| peeked.lexicon === Lexicon.NUMERIC
|| peeked.lexicon === Lexicon.ALPHANUMERIC
|| peeked.lexicon === Lexicon.COMPARATOR) {
if (peeked.lexicon === Lexicon.COMPARATOR)
peeked.confirm();
let toev: Token[] = [];
while (i + 1 < tokens.length) {
let token = getToken();
if (token.lexicon === Lexicon.TERMINATOR) break;
toev.push(token);
}
let res = Stack(toev, variables, true);
if (peeked.lexicon === Lexicon.COMPARATOR)
retValue = retValue == res;
else
retValue += res;
}
return retValue;
}
};
export function Interpret(source: string) {
return Stack(Lexicate(source));
};
\ No newline at end of file
import { isAlphaNumeric } from "./Util";
import { Throw, Error } from "./Error";
enum Lexicon {
// error handling
UNKNOWN,
// 0-9
NUMERIC,
// a-Z 0-9
ALPHANUMERIC,
// any plausible string
STRING,
// function body '{'
BODY_BEGIN,
// end of body '}'
BODY_END,
// argument list '('
ARGUMENT_BEGIN,
// end of arguments ')'
ARGUMENT_END,
// seperator, ','
DELIMETER,
// child selector, '.'
PERIOD,
// = operator
ASSIGNMENT,
// == operator
COMPARATOR,
// addition operator +
OPERATOR_ADDITION,
// minus operator -
OPERATOR_MINUS,
// multiplication operator *
OPERATOR_MULTIPLICATION,
// division operator /
OPERATOR_DIVISION,
// semicolon
TERMINATOR
};
export interface Token {
raw: string,
lexicon: Lexicon,
body?: String | Number,
line: Number,
column: Number
};
export interface PeekedToken extends Token {
confirm: Function
};
enum State {
NORMAL,
CAPTURE,
SQ_STRING,
DQ_STRING,
COMMENT,
ML_COMMENT
};
export function Lexicate(source: string): Token[] {
let tokens: Token[] = [];
let line = 1, column = 0;
let state = State.NORMAL;
for (let i=0;i<source.length;i++) {
column++;
let t = source[i];
if (t == '\n') {
line++;
column = 0;
}
if (t === "'" || t === '"') {
if (state === State.SQ_STRING ||
state === State.DQ_STRING) {
state = State.NORMAL;
} else {
state = (t === "'" ? State.SQ_STRING : State.DQ_STRING);
tokens.push({
raw: t,
lexicon: Lexicon.STRING,
body: '',
line, column
});
}
continue;
}
switch (state) {
case State.SQ_STRING:
case State.DQ_STRING:
tokens[tokens.length - 1].raw += t;
tokens[tokens.length - 1].body += t;
break;
case State.NORMAL:
case State.CAPTURE:
switch (t) {
case '{':
tokens.push({
raw: t,
lexicon: Lexicon.BODY_BEGIN,
line, column
});
break;
case '}':
tokens.push({
raw: t,
lexicon: Lexicon.BODY_END,
line, column
});
break;
case '(':
tokens.push({
raw: t,
lexicon: Lexicon.ARGUMENT_BEGIN,
line, column
});
break;
case ')':
tokens.push({
raw: t,
lexicon: Lexicon.ARGUMENT_END,
line, column
});
break;
case ',':
tokens.push({
raw: t,
lexicon: Lexicon.DELIMETER,
line, column
});
break;
case '.':
tokens.push({
raw: t,
lexicon: Lexicon.PERIOD,
line, column
});
break;
case ';':
tokens.push({
raw: t,
lexicon: Lexicon.TERMINATOR,
line, column
});
break;
case '=':
let last = tokens[tokens.length - 1];
if (last && last.lexicon === Lexicon.ASSIGNMENT) {
last.lexicon = Lexicon.COMPARATOR;
last.raw = '==';
break;
}
tokens.push({
raw: t,
lexicon: Lexicon.ASSIGNMENT,
line, column
});
break;
case '+':
tokens.push({
raw: t,
lexicon: Lexicon.OPERATOR_ADDITION,
line, column
});
break;
case '-':
tokens.push({
raw: t,
lexicon: Lexicon.OPERATOR_MINUS,
line, column
});
break;
case '*':
tokens.push({
raw: t,
lexicon: Lexicon.OPERATOR_MULTIPLICATION,
line, column
});
break;
case '/':
tokens.push({
raw: t,
lexicon: Lexicon.OPERATOR_DIVISION,