time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

compile.js (10263B)


      1 import { List as ImmutableList } from "immutable";
      2 import { infixFunctions, unaryFunctions } from "../ast/peggyHelpers.js";
      3 import { ICompileError } from "../errors/IError.js";
      4 import { ImmutableMap } from "../utility/immutableMap.js";
      5 import * as Result from "../utility/result.js";
      6 import { vBool, vNumber, vString } from "../value/index.js";
      7 import { INDEX_LOOKUP_FUNCTION } from "./constants.js";
      8 import * as expression from "./index.js";
      9 function createInitialCompileContext(externals) {
     10     return {
     11         externals,
     12         nameToPos: ImmutableMap(),
     13         locals: ImmutableList(),
     14         size: 0,
     15     };
     16 }
     17 function resolveName(context, ast, name) {
     18     const offset = context.nameToPos.get(name);
     19     if (offset !== undefined) {
     20         return expression.eResolvedSymbol(name, context.size - 1 - offset);
     21     }
     22     const value = context.externals.get(name);
     23     if (value !== undefined) {
     24         return expression.eValue(value);
     25     }
     26     throw new ICompileError(`${name} is not defined`, ast.location);
     27 }
     28 function compileToContent(ast, context) {
     29     switch (ast.type) {
     30         case "Block": {
     31             let currentContext = {
     32                 externals: context.externals,
     33                 nameToPos: context.nameToPos,
     34                 locals: ImmutableList(),
     35                 size: context.size,
     36             };
     37             const statements = [];
     38             for (const astStatement of ast.statements) {
     39                 if ((astStatement.type === "LetStatement" ||
     40                     astStatement.type === "DefunStatement") &&
     41                     astStatement.exported) {
     42                     throw new ICompileError("Exports aren't allowed in blocks", astStatement.location);
     43                 }
     44                 const [statement, newContext] = innerCompileAst(astStatement, currentContext);
     45                 statements.push(statement);
     46                 currentContext = newContext;
     47             }
     48             return [expression.eBlock(statements), context];
     49         }
     50         case "Program": {
     51             let currentContext = {
     52                 externals: context.externals,
     53                 nameToPos: context.nameToPos,
     54                 locals: ImmutableList(),
     55                 size: context.size,
     56             };
     57             const statements = [];
     58             const exports = [];
     59             for (const astStatement of ast.statements) {
     60                 const [statement, newContext] = innerCompileAst(astStatement, currentContext);
     61                 statements.push(statement);
     62                 if ((astStatement.type === "LetStatement" ||
     63                     astStatement.type === "DefunStatement") &&
     64                     astStatement.exported) {
     65                     exports.push(astStatement.variable.value);
     66                 }
     67                 currentContext = newContext;
     68             }
     69             return [expression.eProgram(statements, exports), currentContext];
     70         }
     71         case "DefunStatement":
     72         case "LetStatement": {
     73             const newContext = {
     74                 externals: context.externals,
     75                 nameToPos: context.nameToPos.set(ast.variable.value, context.size),
     76                 locals: context.locals.push(ast.variable.value),
     77                 size: context.size + 1,
     78             };
     79             return [
     80                 expression.eLetStatement(ast.variable.value, innerCompileAst(ast.value, context)[0]),
     81                 newContext,
     82             ];
     83         }
     84         case "Call": {
     85             return [
     86                 expression.eCall(innerCompileAst(ast.fn, context)[0], ast.args.map((arg) => innerCompileAst(arg, context)[0])),
     87                 context,
     88             ];
     89         }
     90         case "InfixCall": {
     91             return [
     92                 expression.eCall({ ast, ...resolveName(context, ast, infixFunctions[ast.op]) }, ast.args.map((arg) => innerCompileAst(arg, context)[0])),
     93                 context,
     94             ];
     95         }
     96         case "UnaryCall":
     97             return [
     98                 expression.eCall({ ast, ...resolveName(context, ast, unaryFunctions[ast.op]) }, [innerCompileAst(ast.arg, context)[0]]),
     99                 context,
    100             ];
    101         case "Pipe":
    102             return [
    103                 expression.eCall(innerCompileAst(ast.fn, context)[0], [
    104                     innerCompileAst(ast.leftArg, context)[0],
    105                     ...ast.rightArgs.map((arg) => innerCompileAst(arg, context)[0]),
    106                 ]),
    107                 context,
    108             ];
    109         case "DotLookup":
    110             return [
    111                 expression.eCall({ ast, ...resolveName(context, ast, INDEX_LOOKUP_FUNCTION) }, [
    112                     innerCompileAst(ast.arg, context)[0],
    113                     { ast, ...expression.eValue(vString(ast.key)) },
    114                 ]),
    115                 context,
    116             ];
    117         case "BracketLookup":
    118             return [
    119                 expression.eCall({ ast, ...resolveName(context, ast, INDEX_LOOKUP_FUNCTION) }, [
    120                     innerCompileAst(ast.arg, context)[0],
    121                     innerCompileAst(ast.key, context)[0],
    122                 ]),
    123                 context,
    124             ];
    125         case "Lambda": {
    126             let newNameToPos = context.nameToPos;
    127             const args = [];
    128             for (let i = 0; i < ast.args.length; i++) {
    129                 const astArg = ast.args[i];
    130                 let arg;
    131                 if (astArg.type === "Identifier") {
    132                     arg = { name: astArg.value, annotation: undefined };
    133                 }
    134                 else if (astArg.type === "IdentifierWithAnnotation") {
    135                     arg = {
    136                         name: astArg.variable,
    137                         annotation: innerCompileAst(astArg.annotation, context)[0],
    138                     };
    139                 }
    140                 else {
    141                     throw new ICompileError(`Internal error: argument ${astArg.type} is not an identifier`, ast.location);
    142                 }
    143                 args.push(arg);
    144                 newNameToPos = newNameToPos.set(arg.name, context.size + i);
    145             }
    146             const innerContext = {
    147                 externals: context.externals,
    148                 nameToPos: newNameToPos,
    149                 locals: ImmutableList(),
    150                 size: context.size + ast.args.length,
    151             };
    152             return [
    153                 expression.eLambda(ast.name, args, innerCompileAst(ast.body, innerContext)[0]),
    154                 context,
    155             ];
    156         }
    157         case "KeyValue":
    158             return [
    159                 expression.eArray([
    160                     innerCompileAst(ast.key, context)[0],
    161                     innerCompileAst(ast.value, context)[0],
    162                 ]),
    163                 context,
    164             ];
    165         case "Ternary":
    166             return [
    167                 expression.eTernary(innerCompileAst(ast.condition, context)[0], innerCompileAst(ast.trueExpression, context)[0], innerCompileAst(ast.falseExpression, context)[0]),
    168                 context,
    169             ];
    170         case "Array":
    171             return [
    172                 expression.eArray(ast.elements.map((statement) => innerCompileAst(statement, context)[0])),
    173                 context,
    174             ];
    175         case "Dict":
    176             return [
    177                 expression.eDict(ast.elements.map((kv) => {
    178                     if (kv.type === "KeyValue") {
    179                         return [
    180                             innerCompileAst(kv.key, context)[0],
    181                             innerCompileAst(kv.value, context)[0],
    182                         ];
    183                     }
    184                     else if (kv.type === "Identifier") {
    185                         const key = { ast: kv, ...expression.eValue(vString(kv.value)) };
    186                         const value = {
    187                             ast: kv,
    188                             ...resolveName(context, kv, kv.value),
    189                         };
    190                         return [key, value];
    191                     }
    192                     else {
    193                         throw new Error(`Internal AST error: unexpected kv ${kv}`);
    194                     }
    195                 })),
    196                 context,
    197             ];
    198         case "Boolean":
    199             return [expression.eValue(vBool(ast.value)), context];
    200         case "Float": {
    201             const value = parseFloat(`${ast.integer}${ast.fractional === null ? "" : `.${ast.fractional}`}${ast.exponent === null ? "" : `e${ast.exponent}`}`);
    202             if (Number.isNaN(value)) {
    203                 throw new ICompileError("Failed to compile a number", ast.location);
    204             }
    205             return [expression.eValue(vNumber(value)), context];
    206         }
    207         case "String":
    208             return [expression.eValue(vString(ast.value)), context];
    209         case "Void":
    210             return [expression.eVoid(), context];
    211         case "Identifier": {
    212             const offset = context.nameToPos.get(ast.value);
    213             if (offset === undefined) {
    214                 return [resolveName(context, ast, ast.value), context];
    215             }
    216             else {
    217                 const result = expression.eResolvedSymbol(ast.value, context.size - 1 - offset);
    218                 return [result, context];
    219             }
    220         }
    221         case "UnitValue": {
    222             const fromUnitFn = resolveName(context, ast, `fromUnit_${ast.unit}`);
    223             return [
    224                 expression.eCall({ ast, ...fromUnitFn }, [
    225                     innerCompileAst(ast.value, context)[0],
    226                 ]),
    227                 context,
    228             ];
    229         }
    230         case "IdentifierWithAnnotation":
    231             throw new ICompileError("Can't compile IdentifierWithAnnotation outside of lambda declaration", ast.location);
    232         default:
    233             throw new Error(`Unsupported AST value ${ast}`);
    234     }
    235 }
    236 function innerCompileAst(ast, context) {
    237     const [content, newContext] = compileToContent(ast, context);
    238     return [
    239         {
    240             ast,
    241             ...content,
    242         },
    243         newContext,
    244     ];
    245 }
    246 export function compileAst(ast, externals) {
    247     try {
    248         const [expression] = innerCompileAst(ast, createInitialCompileContext(externals));
    249         return Result.Ok(expression);
    250     }
    251     catch (err) {
    252         if (err instanceof ICompileError) {
    253             return Result.Err(err);
    254         }
    255         throw err;
    256     }
    257 }
    258 //# sourceMappingURL=compile.js.map