Skip to content

Commit

Permalink
feat: working base #include
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati365 committed Nov 7, 2023
1 parent 2924388 commit 735ec1f
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 60 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,18 @@ npx ts-c ./main.c -ps

## What is not working? 🚧

- [ ] Preprocessor (`#include`, `#define`, `#if` etc.)
- [ ] Linker (ability to compile multiple source files at once)
- [ ] Unions
- [ ] Bitfields
- [ ] Linker (ability to compile multiple source files at once)
- [ ] ... and many other things

## What can be currently compiled?

### Simple macros with constant expressions optimization

```c
#include "file.h"

#define PRINT_SUM 1
#define A 1
#define B 1
Expand Down
33 changes: 24 additions & 9 deletions apps/cli/src/NodeFsIncludeResolver.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import * as E from 'fp-ts/Either';

import {
Expand All @@ -9,13 +11,26 @@ import {
} from '@ts-c-compiler/compiler';

export class NodeFsIncludeResolver implements CInterpreterIncludeResolver {
read(
path: CInterpreterSourcePath,
): E.Either<CPreprocessorError, CInterpreterSourceFile> {
return E.left(
new CPreprocessorError(CPreprocessorErrorCode.CANNOT_INCLUDE_FILE, null, {
name: path.filename,
}),
);
}
read =
(currentFilePath: string) =>
(
path: CInterpreterSourcePath,
): E.Either<CPreprocessorError, CInterpreterSourceFile> => {
const absolutePath = join(dirname(currentFilePath), path.filename);

return E.tryCatch(
() => ({
content: readFileSync(absolutePath, { encoding: 'utf8' }),
absolutePath,
}),
() =>
new CPreprocessorError(
CPreprocessorErrorCode.CANNOT_INCLUDE_FILE,
null,
{
name: absolutePath,
},
),
);
};
}
1 change: 1 addition & 0 deletions apps/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ program
enabled: true,
},
preprocessor: {
currentFilePath: source,
fsIncludeResolver: new NodeFsIncludeResolver(),
},
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NodeLocation } from '@ts-c-compiler/grammar';

import { CInterpreterSourcePath } from '../interpreter';
import { CInterpreterContext, CInterpreterSourcePath } from '../interpreter';

import {
ASTCPreprocessorKind,
ASTCPreprocessorTreeNode,
Expand All @@ -10,4 +11,8 @@ export class ASTCIncludeNode extends ASTCPreprocessorTreeNode {
constructor(loc: NodeLocation, readonly path: CInterpreterSourcePath) {
super(ASTCPreprocessorKind.Include, loc);
}

override exec(ctx: CInterpreterContext): void {
ctx.includeFile(this.path);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { pipe } from 'fp-ts/function';
import * as E from 'fp-ts/Either';

import { unwrapEitherOrThrow } from '@ts-c-compiler/core';
import { Token } from '@ts-c-compiler/lexer';
import { CInterpreterContext, CInterpreterIncludeResolver } from './types';

import type { CPreprocessorMacro } from './types/CPreprocessorMacro';

import { clexer } from '../../parser/lexer/clexer';
import { evalTokens } from './evalTokens';

import { ExpressionResultTreeVisitor } from './ExpressionResultTreeVisitor';
import { CPreprocessorError, CPreprocessorErrorCode } from '../grammar';

export type CInterpreterScope = {
macros: Record<string, CPreprocessorMacro>;
};

export type CContextCreatorConfig = {
scope: CInterpreterScope;
currentFilePath: string;
fsIncludeResolver?: CInterpreterIncludeResolver;
interpretIncludedTokens: (
includedFilePath: string,
) => (tokens: Token[]) => Token[];
};

export const createInterpreterContext = ({
scope,
currentFilePath,
interpretIncludedTokens,
fsIncludeResolver,
}: CContextCreatorConfig) => {
const reduced: Token[] = [];
const ctx: CInterpreterContext = {
evalTokens: evalTokens(scope),
isDefined: (name: string) => name in scope.macros,
defineMacro: (name: string, macro: CPreprocessorMacro) => {
scope.macros[name] = macro;
},
appendFinalTokens: finalTokens => {
reduced.push(...finalTokens);
},
evalExpression: expression => {
const visitor = new ExpressionResultTreeVisitor(ctx);

return visitor.visit(expression).value;
},
includeFile: path => {
if (!fsIncludeResolver) {
throw new CPreprocessorError(
CPreprocessorErrorCode.CANNOT_INCLUDE_FILE,
null,
{ name: path.filename },
);
}

pipe(
E.Do,
E.bind('resolverOutput', () =>
fsIncludeResolver.read(currentFilePath)(path),
),
E.bindW('tokens', ({ resolverOutput }) =>
clexer({})(resolverOutput.content),
),
E.map(({ tokens, resolverOutput }) =>
interpretIncludedTokens(resolverOutput.absolutePath)(tokens),
),
unwrapEitherOrThrow,
ctx.appendFinalTokens,
);
},
};

return {
reduced,
ctx,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,48 @@ import { Token, TokenType } from '@ts-c-compiler/lexer';
import { createCPreprocessorGrammar } from '../grammar';

import { ASTCPreprocessorTreeNode } from '../ast';
import { CPreprocessorConfig, CInterpreterContext } from './types';

import type { CPreprocessorMacro } from './types/CPreprocessorMacro';

import { evalTokens } from './evalTokens';
import { ExpressionResultTreeVisitor } from './ExpressionResultTreeVisitor';

export type CInterpreterScope = {
macros: Record<string, CPreprocessorMacro>;
};

export const interpret = (config: CPreprocessorConfig) => (tokens: Token[]) => {
const reduced: Token[] = [];
const scope: CInterpreterScope = {
macros: {},
};

const ctx: CInterpreterContext = {
config,
evalTokens: evalTokens(scope),
isDefined: (name: string) => name in scope.macros,
defineMacro: (name: string, macro: CPreprocessorMacro) => {
scope.macros[name] = macro;
},
appendFinalTokens: finalTokens => {
reduced.push(...finalTokens);
},
evalExpression: expression => {
const visitor = new ExpressionResultTreeVisitor(ctx);

return visitor.visit(expression).value;
},
import { CPreprocessorConfig } from './types';

import {
createInterpreterContext,
type CInterpreterScope,
} from './createInterpreterContext';

export type CPreprocessorInterpreter = (
config: CPreprocessorConfig & {
forwardedScope?: CInterpreterScope;
},
) => (tokens: Token[]) => Token[];

export const interpret: CPreprocessorInterpreter =
({ forwardedScope, ...config }) =>
tokens => {
const scope: CInterpreterScope = forwardedScope ?? {
macros: {},
};

let { reduced, ctx } = createInterpreterContext({
scope,
currentFilePath: config.currentFilePath,
fsIncludeResolver: config.fsIncludeResolver,
interpretIncludedTokens: includedFilePath =>
interpret({
...config,
forwardedScope: scope,
currentFilePath: includedFilePath,
}),
});

const tree = createCPreprocessorGrammar().process(tokens)
.children[0] as ASTCPreprocessorTreeNode;

tree.exec(ctx);

if (forwardedScope) {
reduced = reduced.filter(token => token.type !== TokenType.EOF);
} else if (R.last(reduced)?.type !== TokenType.EOF) {
reduced.push(new Token(TokenType.EOF, null, null, null));
}

return reduced;
};

const tree = createCPreprocessorGrammar().process(tokens)
.children[0] as ASTCPreprocessorTreeNode;

tree.exec(ctx);

if (R.last(reduced)?.type !== TokenType.EOF) {
reduced.push(new Token(TokenType.EOF, null, null, null));
}

return reduced;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import type {
} from 'frontend/preprocessor/ast';

import type { CPreprocessorMacro } from './CPreprocessorMacro';
import type { CPreprocessorConfig } from './CPreprocessorConfig';
import type { CInterpreterSourcePath } from './CInterpreterIncludeResolver';

export type CInterpreterContext = {
config: CPreprocessorConfig;
isDefined(name: string): boolean;
defineMacro(name: string, macro: CPreprocessorMacro): void;
evalTokens(tokens: Token[]): Token[];
evalExpression(expression: ASTCPreprocessorTreeNode): ASTCExecResult;
appendFinalTokens(tokens: Token[]): void;
includeFile(path: CInterpreterSourcePath): void;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ export type CInterpreterSourcePath = {
};

export type CInterpreterSourceFile = {
absolutePath: string;
content: string;
};

export type CInterpreterIncludeResolver = {
read(
read: (
currentFilePath: string,
) => (
path: CInterpreterSourcePath,
): Either<CPreprocessorError, CInterpreterSourceFile>;
) => Either<CPreprocessorError, CInterpreterSourceFile>;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { CInterpreterIncludeResolver } from './CInterpreterIncludeResolver';

export type CPreprocessorConfig = {
currentFilePath: string;
fsIncludeResolver?: CInterpreterIncludeResolver;
};

0 comments on commit 735ec1f

Please sign in to comment.