|
| 1 | +package docs |
| 2 | + |
| 3 | +import ( |
| 4 | + "sort" |
| 5 | + "strings" |
| 6 | + |
| 7 | + "github.com/vknabel/lithia/ast" |
| 8 | + "github.com/vknabel/lithia/runtime" |
| 9 | +) |
| 10 | + |
| 11 | +var _ runtime.ExternalDefinition = ExternalDocs{} |
| 12 | + |
| 13 | +type ExternalDocs struct{} |
| 14 | + |
| 15 | +func (e ExternalDocs) Lookup(name string, env *runtime.Environment, decl ast.Decl) (runtime.RuntimeValue, bool) { |
| 16 | + switch name { |
| 17 | + case "inspect": |
| 18 | + return e.docsInspectValueFunction(env, decl), true |
| 19 | + default: |
| 20 | + return nil, false |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +func (e ExternalDocs) docsInspectValueFunction(env *runtime.Environment, decl ast.Decl) runtime.PreludeExternFunction { |
| 25 | + return runtime.MakeExternFunction( |
| 26 | + decl, |
| 27 | + func(args []runtime.Evaluatable) (runtime.RuntimeValue, *runtime.RuntimeError) { |
| 28 | + if len(args) != 1 { |
| 29 | + return nil, runtime.NewRuntimeErrorf("inspect takes exactly one argument") |
| 30 | + } |
| 31 | + value, err := args[0].Evaluate() |
| 32 | + if err != nil { |
| 33 | + return nil, err |
| 34 | + } |
| 35 | + return docsInspectValue(value, env) |
| 36 | + }, |
| 37 | + ) |
| 38 | +} |
| 39 | + |
| 40 | +func docsInspectValue(value runtime.RuntimeValue, env *runtime.Environment) (runtime.RuntimeValue, *runtime.RuntimeError) { |
| 41 | + switch value := value.(type) { |
| 42 | + case runtime.PreludeModule: |
| 43 | + sortedKeys := make([]string, 0, len(value.Module.Environment.Scope)) |
| 44 | + for key := range value.Module.Environment.Scope { |
| 45 | + sortedKeys = append(sortedKeys, key) |
| 46 | + } |
| 47 | + sort.Strings(sortedKeys) |
| 48 | + |
| 49 | + children := make([]runtime.RuntimeValue, 0) |
| 50 | + for _, key := range sortedKeys { |
| 51 | + decl := value.Module.Environment.Scope[key] |
| 52 | + lazyChild, err := decl.Evaluate() |
| 53 | + if err != nil { |
| 54 | + return nil, err |
| 55 | + } |
| 56 | + child, err := docsInspectValue(lazyChild, env) |
| 57 | + if err != nil { |
| 58 | + return nil, err |
| 59 | + } |
| 60 | + children = append(children, child) |
| 61 | + } |
| 62 | + |
| 63 | + childrenList, err := env.MakeEagerList(children) |
| 64 | + if err != nil { |
| 65 | + return nil, err |
| 66 | + } |
| 67 | + moduleDocs := []string{} |
| 68 | + for _, file := range value.Module.Decl.Files { |
| 69 | + for _, decl := range file.Declarations { |
| 70 | + if moduleDecl, ok := decl.(ast.DeclModule); ok { |
| 71 | + if len(moduleDecl.Docs.Content) == 0 { |
| 72 | + continue |
| 73 | + } |
| 74 | + moduleDocs = append(moduleDocs, moduleDecl.Docs.Content) |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + return env.MakeDataRuntimeValue("ModuleDocs", map[string]runtime.Evaluatable{ |
| 79 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Module.Name)), |
| 80 | + "types": runtime.NewConstantRuntimeValue(childrenList), |
| 81 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(strings.Join(moduleDocs, "\n"))), |
| 82 | + }) |
| 83 | + case runtime.PreludeDataDecl: |
| 84 | + fieldDocs := make([]runtime.RuntimeValue, 0) |
| 85 | + for _, field := range value.Decl.Fields { |
| 86 | + params := make([]runtime.RuntimeValue, 0) |
| 87 | + for _, param := range field.Parameters { |
| 88 | + params = append(params, runtime.PreludeString(param.Name)) |
| 89 | + } |
| 90 | + paramsList, err := env.MakeEagerList(params) |
| 91 | + if err != nil { |
| 92 | + return nil, err |
| 93 | + } |
| 94 | + doc, err := env.MakeDataRuntimeValue("DataFieldDocs", map[string]runtime.Evaluatable{ |
| 95 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Name)), |
| 96 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Docs.Content)), |
| 97 | + "params": runtime.NewConstantRuntimeValue(paramsList), |
| 98 | + }) |
| 99 | + if err != nil { |
| 100 | + return nil, err |
| 101 | + } |
| 102 | + fieldDocs = append(fieldDocs, doc) |
| 103 | + } |
| 104 | + fieldDocsList, err := env.MakeEagerList(fieldDocs) |
| 105 | + if err != nil { |
| 106 | + return nil, err |
| 107 | + } |
| 108 | + return env.MakeDataRuntimeValue("DataDocs", map[string]runtime.Evaluatable{ |
| 109 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Name)), |
| 110 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Docs.Content)), |
| 111 | + "fields": runtime.NewConstantRuntimeValue(fieldDocsList), |
| 112 | + }) |
| 113 | + case runtime.PreludePrimitiveExternType: |
| 114 | + fieldDocs := make([]runtime.RuntimeValue, 0) |
| 115 | + for _, field := range value.Fields { |
| 116 | + params := make([]runtime.RuntimeValue, 0) |
| 117 | + for _, param := range field.Parameters { |
| 118 | + params = append(params, runtime.PreludeString(param.Name)) |
| 119 | + } |
| 120 | + paramsList, err := env.MakeEagerList(params) |
| 121 | + if err != nil { |
| 122 | + return nil, err |
| 123 | + } |
| 124 | + doc, err := env.MakeDataRuntimeValue("ExternFieldDocs", map[string]runtime.Evaluatable{ |
| 125 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Name)), |
| 126 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Docs.Content)), |
| 127 | + "params": runtime.NewConstantRuntimeValue(paramsList), |
| 128 | + }) |
| 129 | + if err != nil { |
| 130 | + return nil, err |
| 131 | + } |
| 132 | + fieldDocs = append(fieldDocs, doc) |
| 133 | + } |
| 134 | + fieldDocsList, err := env.MakeEagerList(fieldDocs) |
| 135 | + if err != nil { |
| 136 | + return nil, err |
| 137 | + } |
| 138 | + return env.MakeDataRuntimeValue("ExternTypeDocs", map[string]runtime.Evaluatable{ |
| 139 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Name)), |
| 140 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Docs.Content)), |
| 141 | + "fields": runtime.NewConstantRuntimeValue(fieldDocsList), |
| 142 | + }) |
| 143 | + case runtime.PreludeEnumDecl: |
| 144 | + casesDocs := make([]runtime.RuntimeValue, 0) |
| 145 | + for _, enumCaseDecl := range value.Decl.Cases { |
| 146 | + lazyEnumCase, err := value.LookupCase(string(enumCaseDecl.Name)) |
| 147 | + if err != nil { |
| 148 | + return nil, err |
| 149 | + } |
| 150 | + |
| 151 | + enumCase, err := lazyEnumCase.Evaluate() |
| 152 | + if err != nil { |
| 153 | + return nil, err |
| 154 | + } |
| 155 | + |
| 156 | + enumCaseValueDocs, err := docsInspectValue(enumCase, env) |
| 157 | + if err != nil { |
| 158 | + return nil, err |
| 159 | + } |
| 160 | + doc, err := env.MakeDataRuntimeValue("EnumCaseDocs", map[string]runtime.Evaluatable{ |
| 161 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(enumCaseDecl.Name)), |
| 162 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString("")), |
| 163 | + "type": runtime.NewConstantRuntimeValue(enumCaseValueDocs), |
| 164 | + }) |
| 165 | + if err != nil { |
| 166 | + return nil, err |
| 167 | + } |
| 168 | + casesDocs = append(casesDocs, doc) |
| 169 | + } |
| 170 | + casesList, err := env.MakeEagerList(casesDocs) |
| 171 | + if err != nil { |
| 172 | + return nil, err |
| 173 | + } |
| 174 | + return env.MakeDataRuntimeValue("EnumDocs", map[string]runtime.Evaluatable{ |
| 175 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Name)), |
| 176 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Docs.Content)), |
| 177 | + "cases": runtime.NewConstantRuntimeValue(casesList), |
| 178 | + }) |
| 179 | + case runtime.PreludeFuncDecl: |
| 180 | + params := make([]runtime.RuntimeValue, 0) |
| 181 | + for _, param := range value.Decl.Impl.Parameters { |
| 182 | + params = append(params, runtime.PreludeString(param.Name)) |
| 183 | + } |
| 184 | + paramsList, err := env.MakeEagerList(params) |
| 185 | + if err != nil { |
| 186 | + return nil, err |
| 187 | + } |
| 188 | + return env.MakeDataRuntimeValue("FunctionDocs", map[string]runtime.Evaluatable{ |
| 189 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Name)), |
| 190 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Docs.Content)), |
| 191 | + "params": runtime.NewConstantRuntimeValue(paramsList), |
| 192 | + }) |
| 193 | + case runtime.PreludeExternFunction: |
| 194 | + params := make([]runtime.RuntimeValue, 0) |
| 195 | + for _, param := range value.Decl.Parameters { |
| 196 | + params = append(params, runtime.PreludeString(param.Name)) |
| 197 | + } |
| 198 | + paramsList, err := env.MakeEagerList(params) |
| 199 | + if err != nil { |
| 200 | + return nil, err |
| 201 | + } |
| 202 | + return env.MakeDataRuntimeValue("ExternFunctionDocs", map[string]runtime.Evaluatable{ |
| 203 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Name)), |
| 204 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(value.Decl.Docs.Content)), |
| 205 | + "params": runtime.NewConstantRuntimeValue(paramsList), |
| 206 | + }) |
| 207 | + |
| 208 | + case runtime.RuntimeType: |
| 209 | + decl, err := value.Declaration() |
| 210 | + if err != nil { |
| 211 | + return nil, err |
| 212 | + } |
| 213 | + switch decl := decl.(type) { |
| 214 | + case ast.DeclExternType: |
| 215 | + fieldDocs := make([]runtime.RuntimeValue, 0) |
| 216 | + for _, field := range decl.Fields { |
| 217 | + params := make([]runtime.RuntimeValue, 0) |
| 218 | + for _, param := range field.Parameters { |
| 219 | + params = append(params, runtime.PreludeString(param.Name)) |
| 220 | + } |
| 221 | + paramsList, err := env.MakeEagerList(params) |
| 222 | + if err != nil { |
| 223 | + return nil, err |
| 224 | + } |
| 225 | + doc, err := env.MakeDataRuntimeValue("ExternFieldDocs", map[string]runtime.Evaluatable{ |
| 226 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Name)), |
| 227 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(field.Docs.Content)), |
| 228 | + "params": runtime.NewConstantRuntimeValue(paramsList), |
| 229 | + }) |
| 230 | + if err != nil { |
| 231 | + return nil, err |
| 232 | + } |
| 233 | + fieldDocs = append(fieldDocs, doc) |
| 234 | + } |
| 235 | + fieldDocsList, err := env.MakeEagerList(fieldDocs) |
| 236 | + if err != nil { |
| 237 | + return nil, err |
| 238 | + } |
| 239 | + return env.MakeDataRuntimeValue("ExternTypeDocs", map[string]runtime.Evaluatable{ |
| 240 | + "name": runtime.NewConstantRuntimeValue(runtime.PreludeString(decl.Name)), |
| 241 | + "docs": runtime.NewConstantRuntimeValue(runtime.PreludeString(decl.Docs.Content)), |
| 242 | + "fields": runtime.NewConstantRuntimeValue(fieldDocsList), |
| 243 | + }) |
| 244 | + default: |
| 245 | + return env.MakeDataRuntimeValue("None", map[string]runtime.Evaluatable{}) |
| 246 | + } |
| 247 | + // case DocumentedRuntimeValue: |
| 248 | + // docs := value.GetDocs() |
| 249 | + // return env.MakeDataRuntimeValue("ExternDocs", map[string]Evaluatable{ |
| 250 | + // "name": runtime.NewConstantRuntimeValue(PreludeString(docs.name)), |
| 251 | + // "docs": runtime.NewConstantRuntimeValue(PreludeString(docs.docs)), |
| 252 | + // }) |
| 253 | + default: |
| 254 | + return env.MakeDataRuntimeValue("None", map[string]runtime.Evaluatable{}) |
| 255 | + } |
| 256 | +} |
0 commit comments