From a37805fe6791125733808feb003fa118fcc1ae69 Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 08:04:08 +0200 Subject: [PATCH 1/6] refactor: naming convention for classes --- src/kin.ts | 14 ++++++++++++-- src/runtime/eval/expressions.ts | 6 +++--- src/runtime/eval/statements.ts | 2 +- src/runtime/interpreter.ts | 30 +++++++++++++----------------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/kin.ts b/src/kin.ts index c41c454..f15de44 100644 --- a/src/kin.ts +++ b/src/kin.ts @@ -10,6 +10,8 @@ import { LogError, LogMessage } from './lib/log'; import * as readline from 'readline/promises'; import { readFileSync } from 'fs'; +import { Interpreter } from './runtime/interpreter'; +import { createGlobalEnv } from './runtime/globals'; const rl = readline.createInterface({ input: process.stdin, @@ -28,7 +30,11 @@ async function run(filename: string): Promise { try { const input: string = readFileSync(filename, 'utf-8'); const parser = new Parser(); - LogMessage(parser.produceAST(input)); + const interpreter = new Interpreter(); + const env = createGlobalEnv(); // global environment + const program = parser.produceAST(input); + const results = interpreter.evaluate(program, env); + console.log(JSON.stringify(results)); } catch (error) { const err = error as Error; LogError(err); @@ -49,7 +55,11 @@ async function repl() { try { const parser = new Parser(); - LogMessage(parser.produceAST(input)); + const interpreter = new Interpreter(); + const env = createGlobalEnv(); // global environment + const program = parser.produceAST(input); + const results = interpreter.evaluate(program, env); + console.log(JSON.stringify(results)); } catch (error: unknown) { const err = error as Error; LogError(err); diff --git a/src/runtime/eval/expressions.ts b/src/runtime/eval/expressions.ts index dc366d3..55cfa07 100644 --- a/src/runtime/eval/expressions.ts +++ b/src/runtime/eval/expressions.ts @@ -1,4 +1,4 @@ -import { CallExpr } from '../../parser/ast'; +import { BinaryExpr, CallExpr } from '../../parser/ast'; import { RuntimeVal } from '../values'; import { Identifier, @@ -9,13 +9,13 @@ import { import Environment from '../environment'; -export default class Eval_expr { +export default class EvalExpr { public static eval_identifier( ident: Identifier, env: Environment, ): RuntimeVal {} public static eval_binary_expr( - node: AssignmentExpr, + node: BinaryExpr, env: Environment, ): RuntimeVal {} public static eval_assignment( diff --git a/src/runtime/eval/statements.ts b/src/runtime/eval/statements.ts index 11a5f24..190601d 100644 --- a/src/runtime/eval/statements.ts +++ b/src/runtime/eval/statements.ts @@ -9,7 +9,7 @@ import { import Environment from '../environment'; import { RuntimeVal } from '../values'; -export default class eval_stmt { +export default class EvalStmt { public static eval_program(program: Program, env: Environment): RuntimeVal {} public static eval_function_declaration( declaration: FunctionDeclaration, diff --git a/src/runtime/interpreter.ts b/src/runtime/interpreter.ts index 9731d84..005f7ba 100644 --- a/src/runtime/interpreter.ts +++ b/src/runtime/interpreter.ts @@ -17,15 +17,15 @@ import { } from '../parser/ast'; import Environment from './environment'; -import Eval_expr from './eval/expressions'; -import Eval_stmt from './eval/statements'; +import EvalExpr from './eval/expressions'; +import EvalStmt from './eval/statements'; import { LogError } from '../lib/log'; export class Interpreter { public evaluate(astNode: Stmt, env: Environment): RuntimeVal { switch (astNode.kind) { case 'Program': - return Eval_stmt.eval_program(astNode as Program, env); + return EvalStmt.eval_program(astNode as Program, env); case 'NumericLiteral': return { value: (astNode as NumericLiteral).value, @@ -37,36 +37,32 @@ export class Interpreter { type: 'string', } as StringVal; case 'Identifier': - return Eval_expr.eval_identifier(astNode as Identifier, env); + return EvalExpr.eval_identifier(astNode as Identifier, env); case 'ObjectLiteral': - return Eval_expr.eval_object_expr(astNode as ObjectLiteral, env); + return EvalExpr.eval_object_expr(astNode as ObjectLiteral, env); case 'CallExpression': - return Eval_expr.eval_call_expr(astNode as CallExpr, env); + return EvalExpr.eval_call_expr(astNode as CallExpr, env); case 'AssignmentExpression': - return Eval_expr.eval_assignment(astNode as AssignmentExpr, env); + return EvalExpr.eval_assignment(astNode as AssignmentExpr, env); case 'BinaryExpr': - return Eval_expr.eval_binary_expr(astNode as BinaryExpr, env); + return EvalExpr.eval_binary_expr(astNode as BinaryExpr, env); case 'MemberExpression': - return Eval_expr.eval_member_expr( - env, - undefined, - astNode as MemberExpr, - ); + return EvalExpr.eval_member_expr(env, undefined, astNode as MemberExpr); // Handle statements case 'ConditionalStatement': - return Eval_stmt.eval_conditional_statement( + return EvalStmt.eval_conditional_statement( astNode as ConditionalStmt, env, ); case 'LoopStatement': - return Eval_stmt.eval_for_statement(astNode as LoopStatement, env); + return EvalStmt.eval_for_statement(astNode as LoopStatement, env); case 'VariableDeclaration': - return Eval_stmt.eval_val_declaration( + return EvalStmt.eval_val_declaration( astNode as VariableDeclaration, env, ); case 'FunctionDeclaration': - return Eval_stmt.eval_function_declaration( + return EvalStmt.eval_function_declaration( astNode as FunctionDeclaration, env, ); From a0f4a88b5f5f79f6c23335ddc60778705edc12fa Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 17:10:02 +0200 Subject: [PATCH 2/6] feat: support for floats and exponential ^ operator --- src/parser/parser.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 514d65d..31ca100 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -165,6 +165,7 @@ export default class Parser { return identifier_expr; case TokenType.INTEGER: + case TokenType.FLOAT: const nbr_literal: Expr = { kind: 'NumericLiteral', value: parseFloat(this.eat().lexeme), @@ -188,10 +189,6 @@ export default class Parser { ); // closing paren return value; - case TokenType.TANGA: - LogError( - `On line ${this.at().line} : Kin Error : porogaramu_ntoya niyo yonyine ishobora gutanga ikintu`, - ); default: LogError( `On line ${this.at().line}: Kin Error: Unexpected token ${this.at().lexeme}`, @@ -308,7 +305,7 @@ export default class Parser { private parse_multiplicative_expr(): Expr { let left = this.parse_call_member_expr(); - while (['/', '*', '%'].includes(this.at().lexeme)) { + while (['/', '*', '%', '^'].includes(this.at().lexeme)) { const operator = this.eat().lexeme; const right = this.parse_call_member_expr(); left = { From bdead43fb633a9025997159801de79ef92d6307c Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 17:10:48 +0200 Subject: [PATCH 3/6] feat: evaluation of kin's expressions --- src/runtime/eval/expressions.ts | 214 ++++++++++++++++++++++++++++++-- 1 file changed, 207 insertions(+), 7 deletions(-) diff --git a/src/runtime/eval/expressions.ts b/src/runtime/eval/expressions.ts index 55cfa07..a1dbc59 100644 --- a/src/runtime/eval/expressions.ts +++ b/src/runtime/eval/expressions.ts @@ -1,5 +1,22 @@ +/*********************************************************************** + * Expressions Evaluation * + * Responsible of Kin's Expressions Evaluation * + ***********************************************************************/ + import { BinaryExpr, CallExpr } from '../../parser/ast'; -import { RuntimeVal } from '../values'; +import { + BooleanVal, + FunctionValue, + MK_BOOL, + MK_NULL, + MK_NUMBER, + NativeFnValue, + NullVal, + NumberVal, + ObjectVal, + RuntimeVal, + StringVal, +} from '../values'; import { Identifier, AssignmentExpr, @@ -8,28 +25,211 @@ import { } from '../../parser/ast'; import Environment from '../environment'; +import { Interpreter } from '../interpreter'; export default class EvalExpr { public static eval_identifier( ident: Identifier, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + const val = env.lookupVar(ident.symbol); + return val; + } + public static eval_binary_expr( node: BinaryExpr, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + const lhs = Interpreter.evaluate(node.left, env); + const rhs = Interpreter.evaluate(node.right, env); + return this.eval_numeric_binary_expr( + lhs as RuntimeVal, + rhs as RuntimeVal, + node.operator, + ); + } + public static eval_assignment( node: AssignmentExpr, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + if (node.assigne.kind === 'MemberExpression') + return this.eval_member_expr(env, node); + if (node.assigne.kind !== 'Identifier') + throw `Invalid left-hand-side expression: ${JSON.stringify(node.assigne)}.`; + + const varname = (node.assigne as Identifier).symbol; + + return env.assignVar(varname, Interpreter.evaluate(node.value, env)); + } + public static eval_object_expr( obj: ObjectLiteral, env: Environment, - ): RuntimeVal {} - public static eval_call_expr(expr: CallExpr, env: Environment): RuntimeVal {} + ): RuntimeVal { + const object = { type: 'object', properties: new Map() } as ObjectVal; + + for (const { key, value } of obj.properties) { + const runtimeVal = + value == undefined + ? env.lookupVar(key) + : Interpreter.evaluate(value, env); + + object.properties.set(key, runtimeVal); + } + + return object; + } + + public static eval_call_expr(expr: CallExpr, env: Environment): RuntimeVal { + const args = expr.args.map((arg) => Interpreter.evaluate(arg, env)); + const fn = Interpreter.evaluate(expr.caller, env); + + if (fn.type == 'native-fn') { + const result = (fn as NativeFnValue).call(args, env); + + return result; + } + + if (fn.type == 'fn') { + const func = fn as FunctionValue; + const scope = new Environment(func.declarationEnv); + + // Create the variables for the parameters list + for (let i = 0; i < func.parameters.length; i++) { + // TODO check the bounds here + // verify arity of function + const varname = func.parameters[i]; + scope.declareVar(varname, args[i], false); + } + + // Evaluate the function body line by line + for (const stmt of func.body) { + Interpreter.evaluate(stmt, scope); + } + + let result: RuntimeVal = MK_NULL(); + + if (func.return) { + result = Interpreter.evaluate(func.return, scope); + } + + return result; + } + + throw 'Cannot call value that is not a function: ' + JSON.stringify(fn); + } + public static eval_member_expr( env: Environment, node?: AssignmentExpr, expr?: MemberExpr, - ): RuntimeVal {} + ): RuntimeVal { + if (expr) { + const variable = env.lookupOrMutObject(expr); + return variable; + } else if (node) { + const variable = env.lookupOrMutObject( + node.assigne as MemberExpr, + Interpreter.evaluate(node.value, env), + ); + + return variable; + } else { + throw `Evaluating a member expression is not possible without a member or assignment expression.`; + } + } + + private static eval_numeric_binary_expr( + lhs: RuntimeVal, + rhs: RuntimeVal, + operator: string, + ): RuntimeVal { + if (operator === '!=') { + return this.equals(lhs, rhs, false); + } else if (operator === '==') { + return this.equals(lhs, rhs, true); + } else if (operator === '&&') { + const llhs = lhs as BooleanVal; + const rrhs = rhs as BooleanVal; + + return MK_BOOL(llhs.value && rrhs.value); + } else if (operator === '||') { + const llhs = lhs as BooleanVal; + const rrhs = rhs as BooleanVal; + + return MK_BOOL(llhs.value || rrhs.value); + } else if (lhs.type === 'number' && rhs.type === 'number') { + const llhs = lhs as NumberVal; + const rrhs = rhs as NumberVal; + + switch (operator) { + case '+': + return MK_NUMBER(llhs.value + rrhs.value); + case '-': + return MK_NUMBER(llhs.value - rrhs.value); + case '*': + return MK_NUMBER(llhs.value * rrhs.value); + case '/': + return MK_NUMBER(llhs.value / rrhs.value); + case '^': + return MK_NUMBER(llhs.value ** rrhs.value); + case '%': + return MK_NUMBER(llhs.value % rrhs.value); + case '<': + return MK_BOOL(llhs.value < rrhs.value); + case '>': + return MK_BOOL(llhs.value > rrhs.value); + case '<=': + return MK_BOOL(llhs.value <= rrhs.value); + case '>=': + return MK_BOOL(llhs.value >= rrhs.value); + default: + throw `Unknown operator provided in operation: ${lhs}, ${rhs}.`; + } + } else { + return MK_NULL(); + } + } + + private static equals( + lhs: RuntimeVal, + rhs: RuntimeVal, + strict: boolean, + ): RuntimeVal { + const compare = strict + ? (a: unknown, b: unknown) => a === b + : (a: unknown, b: unknown) => a !== b; + + switch (lhs.type) { + case 'boolean': + return MK_BOOL( + compare((lhs as BooleanVal).value, (rhs as BooleanVal).value), + ); + case 'number': + return MK_BOOL( + compare((lhs as NumberVal).value, (rhs as NumberVal).value), + ); + case 'string': + return MK_BOOL( + compare((lhs as StringVal).value, (rhs as StringVal).value), + ); + case 'fn': + return MK_BOOL( + compare((lhs as FunctionValue).body, (rhs as FunctionValue).body), + ); + case 'native-fn': + return MK_BOOL( + compare((lhs as NativeFnValue).call, (rhs as NativeFnValue).call), + ); + case 'null': + return MK_BOOL(compare((lhs as NullVal).value, (rhs as NullVal).value)); + case 'object': + return MK_BOOL( + compare((lhs as ObjectVal).properties, (rhs as ObjectVal).properties), + ); + default: + throw `RunTime: Unhandled type in equals function: ${lhs}, ${rhs}`; + } + } } From bf3a449a7676cc8a4eea7b3078fd866e21bf4aa0 Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 17:11:08 +0200 Subject: [PATCH 4/6] feat: evaluation of kin's statements --- src/runtime/eval/statements.ts | 108 ++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/src/runtime/eval/statements.ts b/src/runtime/eval/statements.ts index 190601d..a3fbb1a 100644 --- a/src/runtime/eval/statements.ts +++ b/src/runtime/eval/statements.ts @@ -1,3 +1,8 @@ +/*********************************************************************** + * Statements Evaluation * + * Responsible of Kin's Statements Evaluation * + ***********************************************************************/ + import { ConditionalStmt, FunctionDeclaration, @@ -7,29 +12,118 @@ import { VariableDeclaration, } from '../../parser/ast'; import Environment from '../environment'; -import { RuntimeVal } from '../values'; +import { Interpreter } from '../interpreter'; +import { BooleanVal, FunctionValue, MK_NULL, RuntimeVal } from '../values'; export default class EvalStmt { - public static eval_program(program: Program, env: Environment): RuntimeVal {} + public static eval_program(program: Program, env: Environment): RuntimeVal { + let lastEvaluated: RuntimeVal = MK_NULL(); + + for (const statement of program.body) { + lastEvaluated = Interpreter.evaluate(statement, env); + } + + return lastEvaluated; + } public static eval_function_declaration( declaration: FunctionDeclaration, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + // Create new function scope + const fn = { + type: 'fn', + name: declaration.name, + parameters: declaration.parameters, + declarationEnv: env, + body: declaration.body, + return: declaration.return, + } as FunctionValue; + + return env.declareVar(declaration.name, fn, true); + } public static eval_val_declaration( declaration: VariableDeclaration, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + const value = declaration.value + ? Interpreter.evaluate(declaration.value, env) + : MK_NULL(); + + return env.declareVar(declaration.identifier, value, declaration.constant); + } public static eval_conditional_statement( declaration: ConditionalStmt, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + const test = Interpreter.evaluate(declaration.condition, env); + if ((test as BooleanVal).value === true) { + return this.eval_body(declaration.body, env); + } else if (declaration.alternate) { + return this.eval_body(declaration.alternate, env); + } else { + return MK_NULL(); + } + } public static eval_for_statement( declaration: LoopStatement, env: Environment, - ): RuntimeVal {} + ): RuntimeVal { + env = new Environment(env); + const body = declaration.body; + + let test = Interpreter.evaluate(declaration.condition, env); + + if ((test as BooleanVal).value !== true) return MK_NULL(); // The loop didn't start + while ((test as BooleanVal).value) { + this.eval_body(body, new Environment(env), false); + test = Interpreter.evaluate(declaration.condition, env); + } + + return MK_NULL(); + } public static eval_body( body: Stmt[], env: Environment, newEnv: boolean = true, - ): RuntimeVal {} + ): RuntimeVal { + let scope: Environment; + + if (newEnv) { + scope = new Environment(env); + } else { + scope = env; + } + let result: RuntimeVal = MK_NULL(); + + // Evaluate the if body line by line + for (const stmt of body) { + // if((stmt as Identifier).symbol === 'continue') return result; + result = Interpreter.evaluate(stmt, scope); + } + + return result; + } + + private eval_body( + body: Stmt[], + env: Environment, + newEnv: boolean = true, + ): RuntimeVal { + let scope: Environment; + + if (newEnv) { + scope = new Environment(env); + } else { + scope = env; + } + let result: RuntimeVal = MK_NULL(); + + // Evaluate the if body line by line + for (const stmt of body) { + // if((stmt as Identifier).symbol === 'continue') return result; + result = Interpreter.evaluate(stmt, scope); + } + + return result; + } } From c6809c46cd3edbeaaba6882a9706291ba4450a25 Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 17:11:43 +0200 Subject: [PATCH 5/6] feat: walking the kin's AST --- src/runtime/environment.ts | 13 +++++++++++-- src/runtime/globals.ts | 2 ++ src/runtime/interpreter.ts | 9 +++++++-- src/runtime/print.ts | 10 ++++++++-- src/runtime/values.ts | 8 +++++++- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/runtime/environment.ts b/src/runtime/environment.ts index c15b32f..35c6759 100644 --- a/src/runtime/environment.ts +++ b/src/runtime/environment.ts @@ -1,4 +1,10 @@ -import { Identifier, MemberExpr } from '../parser/ast'; +/******************************************************************************************* + * Kin's Environment * + * In which environment are we in? variables accessible - * + * in current scope and other questions like this are solved by Kin's Environment * + *******************************************************************************************/ + +import { Identifier, MemberExpr, NumericLiteral } from '../parser/ast'; import { ObjectVal, RuntimeVal } from './values'; export default class Environment { @@ -61,7 +67,10 @@ export default class Environment { const prop = property ? property.symbol : (expr.property as Identifier).symbol; - const currentProp = (expr.property as Identifier).symbol; + const currentProp = + expr.property.kind == 'Identifier' + ? (expr.property as Identifier).symbol + : (expr.property as NumericLiteral).value.toString(); if (value) pastVal.properties.set(prop, value); diff --git a/src/runtime/globals.ts b/src/runtime/globals.ts index 4c0c80a..94584ae 100644 --- a/src/runtime/globals.ts +++ b/src/runtime/globals.ts @@ -65,6 +65,8 @@ export function createGlobalEnv(): Environment { try { const result = rl.question(cmd); if (result !== null) { + const numberRegex = /^-?\d+(\.\d*)?$/; // regex for numbers and floats + if (numberRegex.test(result)) return MK_NUMBER(Number(result)); return MK_STRING(result); } else { return MK_NULL(); diff --git a/src/runtime/interpreter.ts b/src/runtime/interpreter.ts index 005f7ba..376dcca 100644 --- a/src/runtime/interpreter.ts +++ b/src/runtime/interpreter.ts @@ -1,3 +1,8 @@ +/************************************************************************************************************* + * Interpreter * + * Kin's Interpreter entry point, for walking Kin's AST by it's evaluate static method * + *************************************************************************************************************/ + import { NumberVal, RuntimeVal, StringVal } from './values'; import { AssignmentExpr, @@ -22,7 +27,7 @@ import EvalStmt from './eval/statements'; import { LogError } from '../lib/log'; export class Interpreter { - public evaluate(astNode: Stmt, env: Environment): RuntimeVal { + public static evaluate(astNode: Stmt, env: Environment): RuntimeVal { switch (astNode.kind) { case 'Program': return EvalStmt.eval_program(astNode as Program, env); @@ -68,7 +73,7 @@ export class Interpreter { ); default: LogError( - 'Kin Error: AST of unknown kind found. Cannot evaluate. Exiting. \n Please report this to Kin developers', + 'Kin Error: AST of unknown kind found. Cannot evaluate. Exiting. \n Please report this to Kin developers \n', astNode, ); process.exit(0); diff --git a/src/runtime/print.ts b/src/runtime/print.ts index 488dc8a..78216d1 100644 --- a/src/runtime/print.ts +++ b/src/runtime/print.ts @@ -1,3 +1,8 @@ +/******************************************************************** + * print values * + * Whenever we want to write something to the console * + ********************************************************************/ + import { RuntimeVal, StringVal, @@ -11,11 +16,12 @@ import { import { LogMessage } from '../lib/log'; export function printValues(args: Array) { + let output = ''; for (let i = 0; i < args.length; i++) { const arg = args[i]; - - LogMessage(matchType(arg)); + output += matchType(arg); } + LogMessage(output); } export function matchType(arg: RuntimeVal) { diff --git a/src/runtime/values.ts b/src/runtime/values.ts index d4e516b..0caf369 100644 --- a/src/runtime/values.ts +++ b/src/runtime/values.ts @@ -1,4 +1,9 @@ -import { Stmt } from '../parser/ast'; +/*********************************************************************************************** + * Runtime Values * + * Kin's runtime values, responsible of defining Runtime values types * + ***********************************************************************************************/ + +import { Expr, Stmt } from '../parser/ast'; import Environment from './environment'; export type ValueType = @@ -45,6 +50,7 @@ export interface FunctionValue extends RuntimeVal { parameters: string[]; declarationEnv: Environment; body: Stmt[]; + return?: Expr; } export type FunctionCall = (args: RuntimeVal[], env: Environment) => RuntimeVal; From ccee1703a9c14884120e3ff87df7bcad6604314b Mon Sep 17 00:00:00 2001 From: MURANGWA Pacifique Date: Thu, 8 Feb 2024 17:12:32 +0200 Subject: [PATCH 6/6] fix: no support for recursion --- examples/factorial.kin | 15 --------------- examples/fibonacci.kin | 16 ---------------- examples/fizzbuzz.kin | 2 ++ src/kin.ts | 42 +++++------------------------------------- 4 files changed, 7 insertions(+), 68 deletions(-) delete mode 100644 examples/factorial.kin delete mode 100644 examples/fibonacci.kin diff --git a/examples/factorial.kin b/examples/factorial.kin deleted file mode 100644 index eeac7d0..0000000 --- a/examples/factorial.kin +++ /dev/null @@ -1,15 +0,0 @@ -# Factorial -# Get input from a user, prints it's factorial -# - - -porogaramu_ntoya factorial(nbr) { - niba (nbr == 1) { - tanga 1 - } - tanga nbr * factorial(nbr - 1) -} - -reka input_nbr = injiza_amakuru("Enter a number: ") -reka nbr_factorial = factorial(nbr) -tangaza_amakuru(nbr_factorial) diff --git a/examples/fibonacci.kin b/examples/fibonacci.kin deleted file mode 100644 index 0b7eace..0000000 --- a/examples/fibonacci.kin +++ /dev/null @@ -1,16 +0,0 @@ - -# Fibonacci -# Get input from a user, prints it's Fibonacci sequence -# - - -porogaramu_ntoya fibonacci(nbr) { - niba (nbr == 1 || nbr == 0) { - tanga 1 - } - tanga fibonacci(nbr - 1) + fibonacci(nbr - 2) -} - -reka input_nbr = injiza_amakuru("Enter a number: ") -reka nbr_fibonacci = fibonacci(nbr) -tangaza_amakuru(nbr_fibonacci) diff --git a/examples/fizzbuzz.kin b/examples/fizzbuzz.kin index 8798fde..daac00c 100644 --- a/examples/fizzbuzz.kin +++ b/examples/fizzbuzz.kin @@ -20,5 +20,7 @@ subiramo_niba(i <= limit) { response = i } + i = i + 1 + tangaza_amakuru(response) } diff --git a/src/kin.ts b/src/kin.ts index f15de44..b85f5d3 100644 --- a/src/kin.ts +++ b/src/kin.ts @@ -6,64 +6,32 @@ **************************************************************************/ import Parser from './parser/parser'; -import { LogError, LogMessage } from './lib/log'; +import { LogError } from './lib/log'; -import * as readline from 'readline/promises'; import { readFileSync } from 'fs'; import { Interpreter } from './runtime/interpreter'; import { createGlobalEnv } from './runtime/globals'; -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - const file = process.argv[2]; if (file) { run(file); } else { - repl(); + LogError('No file provided'); + process.exit(1); } async function run(filename: string): Promise { try { const input: string = readFileSync(filename, 'utf-8'); const parser = new Parser(); - const interpreter = new Interpreter(); const env = createGlobalEnv(); // global environment const program = parser.produceAST(input); - const results = interpreter.evaluate(program, env); - console.log(JSON.stringify(results)); + Interpreter.evaluate(program, env); + process.exit(0); } catch (error) { const err = error as Error; LogError(err); process.exit(1); } } - -async function repl() { - LogMessage('Kin Repl v0.0'); - - while (true) { - const input = await rl.question('> '); - - // check for no user input or exit keyword. - if (input === '.exit' || input === '.quit' || input === '.q') { - process.exit(0); - } - - try { - const parser = new Parser(); - const interpreter = new Interpreter(); - const env = createGlobalEnv(); // global environment - const program = parser.produceAST(input); - const results = interpreter.evaluate(program, env); - console.log(JSON.stringify(results)); - } catch (error: unknown) { - const err = error as Error; - LogError(err); - process.exit(1); - } - } -}