Skip to content

Commit 8bbfe7a

Browse files
committed
[Prettify] Prototype
Intentionally de-formatted
1 parent 27f5760 commit 8bbfe7a

File tree

1 file changed

+273
-0
lines changed

1 file changed

+273
-0
lines changed

source/server/prettify.hexa

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
// The Hexa Compiler
2+
// Copyright (C) 2024-2025 Oleh Petrenko
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, version 3 of the License.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU Lesser General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
15+
16+
/// Formatting by taking a syntax tree as input
17+
/// Preserves user personal style to some degree
18+
// TODO ^
19+
class Prettify {
20+
new () {
21+
// Initializes state for nested blocks indentation
22+
}
23+
24+
fun stringify(node Node) String {
25+
switch node {
26+
27+
/// `super`
28+
case Super: return "super"
29+
30+
/// `123n`
31+
/// `123u32`
32+
// TODO just add meta to Int or drop Int/Float entirely
33+
// TODO rename to Number and Meta to TokenKind
34+
case MetaInt(number, meta):
35+
return number.toString() + Meta.stringifyPostfix(meta)
36+
37+
/// `declare Alias = Value`
38+
case TypeAlias(alias, value):
39+
40+
/// `storage op= value`
41+
// TODO rename to AssignWith or CompoundAssign
42+
case AssignOp(storage, op, value):
43+
return this.stringify(storage) + ' ' + Token.stringify(op) + '= ' + this.stringify(value)
44+
45+
/// { el[0] ... el[n] }
46+
// TODO `, commaSeparated Bool` for `{a,b}` and `{a:b,c}
47+
// TODO remove `:` here!
48+
case Block(el):
49+
// TODO depth!
50+
`{\n` + el.map(e => this.stringify(e)).join('\n') + `\n}`
51+
52+
/// `if condition[0], ...condition[n] { then } [else { otherwise }]`
53+
case If(condition, then, otherwise, ternary):
54+
if ternary {
55+
return this.stringify(condition[0]) + ' ? ' + this.stringify(then) + ' : ' + this.stringify(otherwise)
56+
}
57+
58+
return `if ` + condition.map(c => this.stringify(c)).join(`, `) + ` ` + this.stringify(then) + (otherwise ? ` else ` + this.stringify(otherwise) : ``)
59+
60+
/// `expr expr` without `{}`
61+
case InlineStatements(el):
62+
63+
/// `return e`
64+
case Return(e):
65+
if let e = e {
66+
return 'return ' + this.stringify(e)
67+
} else {
68+
return 'return'
69+
}
70+
71+
/// `throw e`
72+
case Throw(e):
73+
return 'throw ' + this.stringify(e)
74+
75+
/// `break`
76+
case Break: return "break"
77+
78+
/// `continue`
79+
case Continue: return "continue"
80+
81+
/// postfix ? `e op` : `op e`
82+
// TODO rename to `Unary`
83+
case Unop(op, postfix, e):
84+
if postfix {
85+
return this.stringify(e) + Token.stringify(op)
86+
} else {
87+
return Token.stringify(op) + this.stringify(e)
88+
}
89+
90+
/// `while reason { e }` or if pre == true then `do { e } while reason`
91+
// TODO rename pre to isDoWhile
92+
case While(reason, e, pre):
93+
if pre {
94+
return 'do ' + this.stringify(e) + ' while ' + this.stringify(reason)
95+
} else {
96+
return 'while ' + this.stringify(reason) + ' ' + this.stringify(e)
97+
}
98+
99+
100+
/// `[declare] fun name<T>(vars) rettype { expr }`
101+
//ParametricFunction(name String?, expr Node, vars [Node], retType NodeType, external Bool, params [NodeType]?)
102+
103+
/// `(vars) retType => expr`
104+
// TODO parser `: retType` is really needed at all?
105+
case Arrow(expr, vars, retType):
106+
if let retType = retType {
107+
return '(' + vars.map(v => this.stringify(v)).join(', ') + ') => ' + this.stringify(expr) + ': ' + NodeType.stringify(retType)
108+
} else {
109+
return '(' + vars.map(v => this.stringify(v)).join(', ') + ') => ' + this.stringify(expr)
110+
}
111+
112+
// // var Var[0], ..., Var[n]
113+
// Vars(vars [Node])
114+
//
115+
/// `external class t extends extend implements implement { fields }`
116+
// TODO IDE: hover over `(className)` in the pattern itself must show its type
117+
case Class(className, extend, implement, fields, external, kind):
118+
return 'class ' + className + ' ' + (extend ? 'extends ' + extend : '') + ' ' + (implement ? 'implements ' + implement : '') + ' {\n' + fields.map(f => this.stringify(f)).join('\n') + '\n}'
119+
120+
/// var name T { get { return x } set (v) {} }
121+
// TODO .Var, .Function, .Function
122+
case Property(field, getter, setter):
123+
124+
/// try { expr } catch v[0]: t[0] { catches[0] } ... catch v[n]: t[n] { catches[n] }
125+
case Try(expr, types, values, catches):
126+
return 'try ' + this.stringify(expr) + catches.map((v, i) => 'catch ' + this.stringify(v) + ': ' + NodeType.stringify(types[i]) + ' ' + this.stringify(catches[i])).join(' ')
127+
128+
/// `new T<T> { } (args)` TODO new syntax
129+
case New(path, ofType, args, fields, el, argNames):
130+
return NodeType.stringify(ofType) + `(` + args.map(a => this.stringify(a)).join(', ') + `)`
131+
132+
/// [keys[0] : values[0], ... keys[n] : values[n]]
133+
case Map(keys, values):
134+
return '[' + [for i in keys.length this.stringify(keys[i]) + ': ' + this.stringify(values[i])].join(', ') + ']'
135+
136+
// TODO update names in this doc
137+
/// switch exprs[0], exprs[n] {
138+
/// case cases[0] if guards[0]: conditions[0]
139+
/// case cases[n]: conditions[n]
140+
/// }
141+
case Switch(inputs , expressions , guards , patterns ):
142+
return 'switch ' + [for i in inputs.length this.stringify(inputs[i])].join(', ') + ' {\n' +
143+
[for i in patterns.length 'case ' + this.stringify(patterns[i]) + ': ' + this.stringify(expressions[i])].join('\n') + '\n}'
144+
145+
/// module path[0].path[1].r..path[n] { el }
146+
case Module(path , el ):
147+
148+
/// Not present in syntax, used for typing
149+
case ModuleExports(handle ):
150+
151+
/// import xxx as yyy in "test"
152+
case Import(el , path ):
153+
154+
// TODO unify into `Node.Class` and rename to `ClassLike` or `Prototype`
155+
// ^ avoid `class` word to not look too much focused on OOP
156+
/// enum t : valuesType extends extend { fields[0] ... fields[n] }
157+
case Enum(
158+
enumType/*TODO uniq name classType etc for easy bind*/ ,
159+
fields ,
160+
valuesType ,
161+
extend
162+
):
163+
return 'enum ' + enumType + (valuesType ? ': ' + NodeType.stringify(valuesType) : '') + (extend ? ' extends ' + extend : '') + ' {\n' + fields.map(f => this.stringify(f)).join('\n') + '\n}'
164+
165+
/// let expr(extract[0], ..., extract[n]) = name
166+
case EnumExtract(path , bind , expr ):
167+
168+
// TODO
169+
// A.B
170+
// A.B(c, d)
171+
// A.B(c: d, d: d)
172+
case EnumConstructor:
173+
// TODO
174+
// case `B()`
175+
// case `B(c, d)`
176+
// case `B(c: d, d: d)` ?
177+
case EnumPattern:
178+
/// `expr is T`
179+
case Is(expr , aType ):
180+
return this.stringify(expr) + ' is ' + NodeType.stringify(aType)
181+
182+
/// `expr as T`
183+
/// `expr as? T`
184+
/// `expr as! T`
185+
case As(expr , kind , toType ):
186+
return this.stringify(expr) + ' as' + Token.stringify(kind) + ' ' + NodeType.stringify(toType)
187+
188+
/// `_`
189+
case Underscore:
190+
return "_"
191+
192+
/// `private` field or type
193+
// TODO drop this
194+
case Private(field ):
195+
return "private " + this.stringify(field)
196+
197+
/// for name in over { statement }
198+
/// for name in over ... range statement
199+
// for index : name in over statement // but no range
200+
// for key : name in over statement // same, just clarification for Map<>
201+
// ^ index is a Var for simple typer implementation
202+
case For(iterator , over , statement , range ):
203+
if let range = range {
204+
return 'for ' + iterator + ' in ' + this.stringify(over) + ' ... ' + this.stringify(range) + ' ' + this.stringify(statement)
205+
}
206+
return 'for ' + iterator + ' in ' + this.stringify(over) + ' ' + this.stringify(statement)
207+
// TODO `range Node?`
208+
//For(name String, over Node, by Node, range Node, index Node?)
209+
210+
/// `nullable ?? alternative`
211+
case Elvis(nullable , alternative ):
212+
return this.stringify(nullable) + ' ?? ' + this.stringify(alternative)
213+
214+
215+
216+
// Smaller ones
217+
case String(s):
218+
// TODO quote types + mirror special symbols
219+
let s = JSON.stringify(s)
220+
return "'\(s)'"
221+
case Ident(name): return name
222+
case Bool(b): return b ? "true": "false"
223+
case Int(s): return s.toString()
224+
case Float(s, meta):
225+
// TODO meta
226+
return s.toString()
227+
case Null: return "null"
228+
case This: return "this"
229+
case Parenthesis(expr): return "(" + this.stringify(expr) + ")"
230+
case Index(expr, index): return this.stringify(expr) + '[' + this.stringify(index) + ']'
231+
case Dot(expr, name): return this.stringify(expr) + '.' + name
232+
case DotUpper(expr, name): return this.stringify(expr) + '.' + name
233+
case Call(e, args, argNames):
234+
let arg = []
235+
for i in args.length {
236+
if let name = argNames[i] {
237+
arg.push(name + ': ' + this.stringify(args [i]))
238+
} else {
239+
arg.push(this.stringify(args [i]))
240+
}
241+
}
242+
return this.stringify(e) + '(' + arg.join(', ') + ')'
243+
case Array(elements): return '[' + [for el in elements this.stringify(el)].join(', ') + ']'
244+
case Binop(a, op, b): return this.stringify(a) + ' ' + Token.stringify(op) + ' ' + this.stringify(b)
245+
case Object(names, el):
246+
return '{' + [for i in el.length names[i] + ': ' + this.stringify(el [i])].join(', ') + '}'
247+
case NodeTypeValue(t):
248+
// TODO full type
249+
return DataHelper.extractTypeName(t)
250+
case Static(f): return 'static ' + this.stringify(f)
251+
case Var(name, t, expr, const, external): {
252+
var body = ''
253+
if let expr = expr {
254+
body = ' = ' + this.stringify(expr)
255+
}
256+
return
257+
(external ? 'declare ': '') +
258+
(const ? 'let ': 'var ') + name + ' ' + NodeType.stringify(t) + body
259+
}
260+
case Function(name, body, vars, returnType, external, variadic):
261+
if name == null {
262+
// TODO `// Anonymous`
263+
return `fun`
264+
}
265+
266+
return 'fun ' + name
267+
case _:
268+
// TODO exhaustive
269+
// console.error('stringify', node)
270+
return "..." + JSON.stringify(node)
271+
}
272+
}
273+
}

0 commit comments

Comments
 (0)