1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | export abstract class ElmType {}
|
9 |
|
10 | export class ElmTypeName extends ElmType {
|
11 | constructor(public name: string) {
|
12 | super();
|
13 | }
|
14 | }
|
15 |
|
16 | export class ElmTypeApp extends ElmType {
|
17 | constructor(public name: string, public args: Array<ElmType>) {
|
18 | super();
|
19 | }
|
20 | }
|
21 |
|
22 | export class ElmTypeRecord extends ElmType {
|
23 | constructor(public fields: Array<ElmFieldDecl>, public typeParam?: string) {
|
24 | super();
|
25 | }
|
26 | }
|
27 |
|
28 |
|
29 |
|
30 | export abstract class ElmDecl {}
|
31 |
|
32 | export class ElmTypeDecl extends ElmDecl {
|
33 | constructor(public name: string,
|
34 | public constructors: Array<string>) {
|
35 | super();
|
36 | }
|
37 | }
|
38 |
|
39 | export class ElmTypeAliasDecl extends ElmDecl {
|
40 | constructor(public name: string,
|
41 | public type: ElmType,
|
42 | public typeParams: Array<string> = []) {
|
43 | super();
|
44 | }
|
45 | }
|
46 |
|
47 | export class ElmFunctionDecl extends ElmDecl {
|
48 | constructor(public name: string,
|
49 | public parameters: Array<ElmParameterDecl>,
|
50 | public returnType: ElmType,
|
51 | public body: ElmExpr) {
|
52 | super();
|
53 | }
|
54 | }
|
55 |
|
56 | export class ElmFieldDecl {
|
57 | constructor(public name: string,
|
58 | public type: ElmType) {}
|
59 | }
|
60 |
|
61 | export class ElmParameterDecl {
|
62 | constructor(public name: string,
|
63 | public type: ElmType) { }
|
64 | }
|
65 |
|
66 |
|
67 |
|
68 | export interface ElmExpr {
|
69 | expr: string;
|
70 | }
|
71 |
|
72 | export function moduleToString(name: string, expose: Array<string>, imports: Array<string>,
|
73 | decls: Array<ElmDecl>) {
|
74 | let warn = '{-\n This file was automatically generated by elm-graphql.\n-}\n';
|
75 | return warn + 'module ' + name + ' exposing (' + expose.join(', ') + ')\n' +
|
76 | imports.map(str => '\nimport ' + str).join('') + '\n\n' +
|
77 | decls.map(declToString).join('\n\n');
|
78 | }
|
79 |
|
80 | export function declToString(decl: ElmDecl): string {
|
81 | if (decl instanceof ElmTypeDecl) {
|
82 | return typeDeclToString(decl);
|
83 | } else if (decl instanceof ElmFunctionDecl) {
|
84 | return funtionToString(decl);
|
85 | } else if (decl instanceof ElmTypeAliasDecl) {
|
86 | return typeAliasDeclToString(decl);
|
87 | } else {
|
88 | throw new Error('unexpected decl: ' + decl.constructor.name + ' ' + JSON.stringify(decl));
|
89 | }
|
90 | }
|
91 |
|
92 | export function typeDeclToString(type: ElmTypeDecl): string {
|
93 | return 'type ' + type.name + '\n' +
|
94 | ' = ' + type.constructors.join('\n | ') + '\n';
|
95 | }
|
96 |
|
97 | export function typeAliasDeclToString(type: ElmTypeAliasDecl): string {
|
98 | return 'type alias ' + type.name + ' ' + type.typeParams.join(' ') + '\n' +
|
99 | ' = ' + typeToString(type.type, 0) + '\n';
|
100 | }
|
101 |
|
102 | export function funtionToString(func: ElmFunctionDecl): string {
|
103 | let paramTypes = func.parameters.map(p => typeToString(p.type, 1)).join(' -> ');
|
104 | let paramNames = func.parameters.map(p => p.name).join(' ');
|
105 | let arrow = paramTypes.length > 0 ? ' -> ' : '';
|
106 | let space = paramTypes.length > 0 ? ' ' : '';
|
107 | return func.name + ' : ' + paramTypes + arrow + typeToString(func.returnType, 0) + '\n' +
|
108 | func.name + space + paramNames + ' =\n ' + exprToString(func.body, 0) + '\n';
|
109 | }
|
110 |
|
111 | function fieldToString(field: ElmFieldDecl, level: number): string {
|
112 | return field.name + ' : ' + typeToString(field.type, level, true) + '\n';
|
113 | }
|
114 |
|
115 | export function typeToString(ty: ElmType, level: number, isField?: boolean): string {
|
116 | if (ty instanceof ElmTypeName) {
|
117 | return ty.name;
|
118 | } else if (ty instanceof ElmTypeApp) {
|
119 | let str = ty.name + ' ' + ty.args.map(arg => typeToString(arg, level)).join(' ');
|
120 | if (isField) {
|
121 | return str;
|
122 | } else {
|
123 | return '(' + str + ')';
|
124 | }
|
125 | } else if (ty instanceof ElmTypeRecord) {
|
126 | let indent = makeIndent(level);
|
127 | let pipe = ty.typeParam ? ty.typeParam + ' | ' : '';
|
128 | return `${indent}{ ` + pipe +
|
129 | ty.fields.map(f => fieldToString(f, level + 1)).join(`${indent} , `) +
|
130 | `${indent}}`;
|
131 | } else {
|
132 | throw new Error('unexpected type: ' + ty.constructor.name + ' ' + JSON.stringify(ty));
|
133 | }
|
134 | }
|
135 |
|
136 | function makeIndent(level: number) {
|
137 | let str = '';
|
138 | for (let i = 0; i < level; i++) {
|
139 | str += ' ';
|
140 | }
|
141 | return str;
|
142 | }
|
143 |
|
144 | export function exprToString(expr: ElmExpr, level: number): string {
|
145 | return expr.expr;
|
146 | }
|