Skip to content

Commit

Permalink
add extern fn to lexer and parser
Browse files Browse the repository at this point in the history
  • Loading branch information
BaseMax committed Mar 5, 2025
1 parent e18c7f4 commit 329e2ae
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 59 deletions.
7 changes: 7 additions & 0 deletions src/phase/lexer/tokenizer/keyword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
1 change: 1 addition & 0 deletions src/phase/lexer/tokenizer/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 29 additions & 0 deletions src/phase/parser/parse/ast/extern.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
8 changes: 8 additions & 0 deletions src/phase/parser/parse/ast/program.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -8,13 +9,15 @@ import { AstFunctionDeclaration } from './function/function_declaration';
export class AstProgram extends AstNode {
errors: string[] = [];
layout: AstLayout | undefined;
externs: AstExtern[];
functions: AstFunctionDeclaration[];
language: LanguageMap;
block: AstBlock;

constructor(language: LanguageMap, block: AstBlock | undefined = undefined) {
super("Program");
this.functions = [];
this.externs = [];
this.language = language;
if (block === undefined) {
this.block = RuntimeBlock.generate();
Expand Down Expand Up @@ -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;
Expand Down
64 changes: 64 additions & 0 deletions src/phase/parser/parse/extern.ts
Original file line number Diff line number Diff line change
@@ -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;
};
12 changes: 12 additions & 0 deletions src/phase/parser/parse/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,25 @@ 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) {
const token: Token = parser.lexer.tokens[parser.index];

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) {
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/block/runtime_bock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 0 additions & 5 deletions src/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@
#include <ctype.h>

// 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) {
Expand Down
47 changes: 0 additions & 47 deletions src/test.json
Original file line number Diff line number Diff line change
@@ -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": [],
Expand Down
8 changes: 4 additions & 4 deletions src/test.salam
Original file line number Diff line number Diff line change
@@ -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 "
Expand Down

0 comments on commit 329e2ae

Please # to comment.