diff --git a/src/phase/lexer/tokenizer/keyword.ts b/src/phase/lexer/tokenizer/keyword.ts index 2c927f53..e9402447 100644 --- a/src/phase/lexer/tokenizer/keyword.ts +++ b/src/phase/lexer/tokenizer/keyword.ts @@ -45,6 +45,13 @@ export const keywordMaps: KeywordMap[] = [ [LanguageID.LanguagePersian]: ["تابع"], }, }, + { + id: TokenKeywordType.TOKEN_EXTERN, + data: { + [LanguageID.LanguageEnglish]: ["extern"], + [LanguageID.LanguagePersian]: ["ارث"], + }, + }, { id: TokenKeywordType.TOKEN_FOR, data: { diff --git a/src/phase/lexer/tokenizer/type.ts b/src/phase/lexer/tokenizer/type.ts index d97f03cc..62a6ac8e 100644 --- a/src/phase/lexer/tokenizer/type.ts +++ b/src/phase/lexer/tokenizer/type.ts @@ -21,6 +21,7 @@ export enum TokenKeywordType { TOKEN_ELSE = "ELSE", TOKEN_PRINT = "PRINT", TOKEN_FN = "FN", + TOKEN_EXTERN = "EXTERN", TOKEN_FOR = "FOR", TOKEN_WHILE = "WHILE", TOKEN_REPEAT = "REPEAT", diff --git a/src/phase/parser/parse/ast/extern.ts b/src/phase/parser/parse/ast/extern.ts new file mode 100644 index 00000000..51c9e6ca --- /dev/null +++ b/src/phase/parser/parse/ast/extern.ts @@ -0,0 +1,29 @@ +import { AstNode } from './node'; +import { AstType } from './expression/type'; +import { stringify } from './../../../../serializer'; +import { AstFunctionArgument } from './function/function_argument'; + +export class AstExtern extends AstNode { + name: string; + args: AstFunctionArgument[]; + return_type: AstType; + generate_name: string; + + constructor(name: string, args: AstFunctionArgument[], return_type: AstType, generate_name: string) { + super("Extern"); + this.name = name; + this.args = args; + this.generate_name = generate_name; + this.return_type = return_type; + } + + stringify(wantsJson: boolean = true): string | object { + const obj: object = { + name: this.name, + args: this.args, + return_type: this.return_type.stringify(false), + generate_name: this.generate_name, + }; + return stringify(obj, wantsJson); + } +}; diff --git a/src/phase/parser/parse/ast/program.ts b/src/phase/parser/parse/ast/program.ts index 878629fe..2918b483 100644 --- a/src/phase/parser/parse/ast/program.ts +++ b/src/phase/parser/parse/ast/program.ts @@ -1,5 +1,6 @@ import { AstNode } from './node'; import { AstBlock } from './block'; +import { AstExtern } from './extern'; import { AstLayout } from './layout/layout'; import { stringify } from './../../../../serializer'; import { LanguageMap } from './../../../../common/language/language'; @@ -8,6 +9,7 @@ import { AstFunctionDeclaration } from './function/function_declaration'; export class AstProgram extends AstNode { errors: string[] = []; layout: AstLayout | undefined; + externs: AstExtern[]; functions: AstFunctionDeclaration[]; language: LanguageMap; block: AstBlock; @@ -15,6 +17,7 @@ export class AstProgram extends AstNode { constructor(language: LanguageMap, block: AstBlock | undefined = undefined) { super("Program"); this.functions = []; + this.externs = []; this.language = language; if (block === undefined) { this.block = RuntimeBlock.generate(); @@ -42,6 +45,11 @@ export class AstProgram extends AstNode { return false; } + pushExtern(extern: AstExtern): boolean { + this.externs.push(extern); + return true; + } + pushFunctionDeclaration(function_declaration: AstFunctionDeclaration): boolean { if (this.hasFunctionDeclaration(function_declaration)) { return false; diff --git a/src/phase/parser/parse/extern.ts b/src/phase/parser/parse/extern.ts new file mode 100644 index 00000000..7e9c08a1 --- /dev/null +++ b/src/phase/parser/parse/extern.ts @@ -0,0 +1,64 @@ +import { Parser } from './parser'; +import { AstBlock } from './ast/block'; +import { AstExtern } from './ast/extern'; +import { parseType } from './expression/type'; +import { AstType } from './ast/expression/type'; +import { AstFunctionArgument } from './ast/function/function_argument'; +import { parserMessageRenderer } from '../../../common/message/message'; +import { ParserMessageKeys } from '../../../common/message/parser/parser'; +import { parserParseFunctionArguments } from './function/function_attributes'; +import { TokenKeywordType, TokenOperatorType } from '../../lexer/tokenizer/type'; + +export function parserParseExtern(parser: Parser, parent_block: AstBlock): AstExtern | undefined { + parser.expect(TokenKeywordType.TOKEN_EXTERN); + + if (parser.skip(TokenKeywordType.TOKEN_FN)) { + + } + if (! parser.currentToken.isKeyword) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FUNCTION_NAME_IS_NOT_VALID_IDENTIFIER)); + return undefined; + } else if (parser.currentToken.isDefinedIdentifier) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FUNCTION_NAME_IS_RESERVED_IN_SALAM)); + return undefined; + } + + const name: string | undefined = parser.currentToken.data?.getValueString(); + if (name === undefined) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FUNCTION_NAME_IS_NOT_VALID)); + return undefined; + } + + // Eating function name + parser.next(); + + const params: AstFunctionArgument[] | undefined = parserParseFunctionArguments(parser); + if (! params) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FUNCTION_PARAMETERS_ARE_NOT_VALID)); + return undefined; + } + + let return_type: AstType | undefined = undefined; + if (parser.skip(TokenOperatorType.TOKEN_MEMBER_POINTER)) { + return_type = parseType(parser); + if (return_type === undefined) { + parser.pushError("Invalid data type as return type of function " + name); + return undefined; + } + } + + if (return_type === undefined) { + return_type = AstType.createVoid(); + } + + parser.expect(TokenOperatorType.TOKEN_COLON); + const generate_name: string | undefined = parser.currentToken.data?.getValueString(); + if (generate_name === undefined) { + parser.pushError("Invalid data as generate name of an externed function."); + return undefined; + } + parser.expect(TokenKeywordType.TOKEN_IDENTIFIER); + + const ast: AstExtern = new AstExtern(name, params, return_type, generate_name); + return ast; +}; diff --git a/src/phase/parser/parse/parse.ts b/src/phase/parser/parse/parse.ts index 5a228f22..e90349d0 100644 --- a/src/phase/parser/parse/parse.ts +++ b/src/phase/parser/parse/parse.ts @@ -7,6 +7,8 @@ import { ParserMessageKeys } from './../../../common/message/parser/parser'; import { AstFunctionDeclaration } from './ast/function/function_declaration'; import { TokenKeywordType, TokenOtherType } from './../../lexer/tokenizer/type'; import { parserParseFunctionDeclaration } from './function/function_declaration'; +import { AstExtern } from './ast/extern'; +import { parserParseExtern } from './extern'; export function parse(parser: Parser): void { while (parser.index < parser.lexer.tokens.length) { @@ -14,6 +16,16 @@ export function parse(parser: Parser): void { if (token.type === TokenOtherType.TOKEN_EOF) { break; + } else if (token.type === TokenKeywordType.TOKEN_EXTERN) { + const extern: AstExtern | undefined = parserParseExtern(parser, parser.ast.block); + if (! extern) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FAILED_TO_PARSE_FUNCTION_STATEMENT)); + break; + } + if (! parser.ast.pushExtern(extern)) { + parser.pushError(parserMessageRenderer(parser.getLanguageId(), ParserMessageKeys.PARSER_FAILED_TO_PARSE_FUNCTION)); + break; + } } else if (token.type === TokenKeywordType.TOKEN_FN) { const function_declaration: AstFunctionDeclaration | undefined = parserParseFunctionDeclaration(parser, parser.ast.block); if (! function_declaration) { diff --git a/src/runtime/block/runtime_bock.ts b/src/runtime/block/runtime_bock.ts index 2f22a399..411dd2bd 100644 --- a/src/runtime/block/runtime_bock.ts +++ b/src/runtime/block/runtime_bock.ts @@ -30,9 +30,9 @@ export class RuntimeBlock { version_type.setSyetem(); block.symbol_table.addSystemSymbol("version", version_type); - const int2str_type: AstType = AstType.createFunction("int2str", "int2str", [new AstFunctionArgument("value", AstType.createInt())], AstType.createString()); - int2str_type.setSyetem(); - block.symbol_table.addSystemSymbol("int2str", int2str_type); + // const int2str_type: AstType = AstType.createFunction("int2str", "int2str", [new AstFunctionArgument("value", AstType.createInt())], AstType.createString()); + // int2str_type.setSyetem(); + // block.symbol_table.addSystemSymbol("int2str", int2str_type); const float2str_type: AstType = AstType.createFunction("float2str", "float2str", [new AstFunctionArgument("value", AstType.createFloat())], AstType.createString()); float2str_type.setSyetem(); block.symbol_table.addSystemSymbol("float2str", float2str_type); diff --git a/src/test.c b/src/test.c index 7a97cc18..cce6b5c9 100644 --- a/src/test.c +++ b/src/test.c @@ -8,15 +8,10 @@ #include // Sign functions -char* _int2str(int a); char* test(); char* main(); // Functions -char* _int2str(int a) { -return ("110"); -} - char* test() { char* a = (char*) malloc(strlen("Hey ") + 1); if (a == NULL) { diff --git a/src/test.json b/src/test.json index 65106af4..9e6be1a2 100644 --- a/src/test.json +++ b/src/test.json @@ -1,53 +1,6 @@ { "type": "Program", "functions": [ - { - "name": "_int2str", - "args": [ - { - "type": "FunctionArgument", - "name": "a", - "value_type": { - "type": "Type", - "is_system": false, - "is_primitive": false, - "type_kind": "int", - "is_pointer": false, - "is_reference": false, - "is_array": false, - "members": [], - "func_args": [] - }, - "is_optional": false - } - ], - "body": { - "children": [ - { - "type": "Return", - "value": { - "type": "ExpressionLiteral", - "value_type": { - "type": "Type", - "is_system": false, - "is_primitive": true, - "type_kind": "string", - "is_pointer": false, - "is_reference": false, - "is_array": false, - "members": [], - "func_args": [] - }, - "value": "110" - } - } - ] - }, - "return_type": { - "type": "Type", - "type_kind": "string" - } - }, { "name": "test", "args": [], diff --git a/src/test.salam b/src/test.salam index a01996ee..ed48a86e 100644 --- a/src/test.salam +++ b/src/test.salam @@ -1,8 +1,8 @@ -// extern +extern fn int2str() -> string: int2str -fn _int2str(int a) -> string: - ret "110" -end +// fn _int2str(int a) -> string: +// ret "110" +// end fn test -> string: string a = "Hey "