diff --git a/src/GigaScript/ast/ast.ts b/src/GigaScript/ast/ast.ts index 9d02b59..3c6e584 100644 --- a/src/GigaScript/ast/ast.ts +++ b/src/GigaScript/ast/ast.ts @@ -5,6 +5,7 @@ export type ASTNodeType = | 'FunctionDeclaration' | 'ReturnStatement' | 'TryCatchStatement' + | 'ThrowStatement' | 'CodeBlockNode' | 'EOF' // Expressions diff --git a/src/GigaScript/ast/statements.ast.ts b/src/GigaScript/ast/statements.ast.ts index 502662b..5d0b909 100644 --- a/src/GigaScript/ast/statements.ast.ts +++ b/src/GigaScript/ast/statements.ast.ts @@ -11,3 +11,8 @@ export interface TryCatchStatement extends STATEMENT { catchBody: CodeBlockNode; errorIdentifier: string; } + +export interface ThrowStatement extends STATEMENT { + kind: 'ThrowStatement'; + message: EXPRESSION; +} diff --git a/src/GigaScript/parser/parser.ts b/src/GigaScript/parser/parser.ts index 2c10320..d1e691a 100644 --- a/src/GigaScript/parser/parser.ts +++ b/src/GigaScript/parser/parser.ts @@ -20,7 +20,11 @@ import { Property, StringLiteral, } from '../ast/literals.ast'; -import { ReturnStatement, TryCatchStatement } from '../ast/statements.ast'; +import { + ReturnStatement, + ThrowStatement, + TryCatchStatement, +} from '../ast/statements.ast'; import { tokenize } from '../lexer/tokenizer'; import { NodeType } from '../nodes'; import { Token, getTokenByTypeEnum } from '../tokens'; @@ -144,6 +148,9 @@ export default class Parser { case NodeType.Return: return this.parseReturnStatement(); + case NodeType.Throw: + return this.parseThrowStatement(); + default: return this.parseExpr(); } @@ -293,6 +300,18 @@ export default class Parser { } as ReturnStatement; } + private parseThrowStatement(): STATEMENT { + const throwTokenPos = this.advance().__GSC._POS; + const message = this.parseExpr(); + + return { + kind: 'ThrowStatement', + message, + start: throwTokenPos.start, + end: message.end, + } as ThrowStatement; + } + // [FUNCTIONS.ARGUMENTS/PARAMETERS] private parseArgs(): Array { this.expect(NodeType.OpenParen, 'before parameter list'); diff --git a/src/GigaScript/runtime/interpreter/eval/statements.ts b/src/GigaScript/runtime/interpreter/eval/statements.ts index dff2842..c11dc3e 100644 --- a/src/GigaScript/runtime/interpreter/eval/statements.ts +++ b/src/GigaScript/runtime/interpreter/eval/statements.ts @@ -1,9 +1,13 @@ -import { Program } from '../../../ast/ast'; +import { CodeBlockNode, Program } from '../../../ast/ast'; import { FunctionDeclaration, VariableDeclaration, } from '../../../ast/declarations.ast'; -import { ReturnStatement } from '../../../ast/statements.ast'; +import { + ReturnStatement, + ThrowStatement, + TryCatchStatement, +} from '../../../ast/statements.ast'; import Environment from '../../env'; import { DataConstructors, DataType, FuncVal, Value } from '../../types'; import { evaluate } from '../interpreter'; @@ -55,3 +59,50 @@ export function evalReturnStatement( return value; } + +export function evalTryCatchStatement( + statement: TryCatchStatement, + env: Environment +): Value { + const tryEnv = new Environment(env.cwd, env); + const catchEnv = new Environment(env.cwd, env); + + try { + return evalCodeBlock(statement.tryBody, tryEnv, false); + } catch (e) { + catchEnv.delcareVar( + statement.errorIdentifier, + DataConstructors.STRING(String(e)), + true + ); + return evalCodeBlock(statement.catchBody, catchEnv, false); + } +} + +export function evalThrowStatement( + statement: ThrowStatement, + env: Environment +): Value<'null', null> { + const message = evaluate(statement.message, env); + + throw message.value; +} + +export function evalCodeBlock( + body: CodeBlockNode, + env: Environment, + createNewEnv = true +): Value { + let scope: Environment; + + if (createNewEnv) scope = new Environment(env.cwd, env); + else scope = env; + + let res: Value = DataConstructors.NULL(); + + for (const stmt of body.body) { + res = evaluate(stmt, scope); + } + + return res; +} diff --git a/src/GigaScript/runtime/interpreter/interpreter.ts b/src/GigaScript/runtime/interpreter/interpreter.ts index e4273fb..a2a8de0 100644 --- a/src/GigaScript/runtime/interpreter/interpreter.ts +++ b/src/GigaScript/runtime/interpreter/interpreter.ts @@ -13,7 +13,11 @@ import { ObjectLiteral, StringLiteral, } from '../../ast/literals.ast'; -import { ReturnStatement } from '../../ast/statements.ast'; +import { + ReturnStatement, + ThrowStatement, + TryCatchStatement, +} from '../../ast/statements.ast'; import { GSError } from '../../util/gserror'; import Environment from '../env'; import { DataType, Value } from '../types'; @@ -30,6 +34,8 @@ import { evalProgram, evalReturnStatement, evalVarDeclaration, + evalTryCatchStatement, + evalThrowStatement, } from './eval/statements'; export function evaluate( @@ -93,6 +99,12 @@ export function evaluate( case 'ReturnStatement': return evalReturnStatement(node as ReturnStatement, env); + case 'TryCatchStatement': + return evalTryCatchStatement(node as TryCatchStatement, env); + + case 'ThrowStatement': + return evalThrowStatement(node as ThrowStatement, env); + // Handle non implemented types default: console.log( diff --git a/tests/main.g b/tests/main.g index 17af27c..8d43df3 100644 --- a/tests/main.g +++ b/tests/main.g @@ -21,6 +21,8 @@ print(obj.complex.foo) try { print("hello world!") + throw 'test' } catch (e) { + print('an error was caught!') print(e) }