├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bundle.json └── ponycc ├── ast ├── _ast_util.pony ├── ast.pony ├── attachments.pony ├── gen │ ├── ast_defs.pony │ ├── ast_gen.pony │ ├── ast_gen_def.pony │ ├── code_gen.pony │ ├── main.pony │ ├── parser_defs.pony │ ├── parser_gen.pony │ └── parser_gen_def.pony ├── program.pony ├── source.pony └── uuid.pony ├── compiler.pony ├── frame ├── _frame_continuation.pony ├── _frame_reactor.pony └── frame.pony ├── pass ├── names │ └── names.pony ├── parse │ ├── _build.pony │ ├── _lexer.pony │ ├── _lexicon.pony │ ├── _parser.pony │ ├── _rule.pony │ ├── parse.pony │ ├── parse_cleanup.pony │ ├── parse_program_files.pony │ └── tk.pony ├── pass.pony ├── pass_error.pony ├── print │ └── print.pony ├── sugar │ └── sugar.pony └── syntax │ └── syntax.pony ├── polyfill └── sort.pony ├── resolve_source_files.pony ├── test ├── fixtures │ ├── names_example.pony │ ├── parse_binary_op.pony │ ├── parse_docs.pony │ ├── parse_error_empty_use.pony │ ├── parse_error_unterminated_lambda.pony │ ├── parse_semicolon.pony │ ├── parse_type.pony │ ├── print_ffi.pony │ ├── print_if.pony │ ├── print_lambda.pony │ ├── print_literal.pony │ ├── print_local.pony │ ├── print_loop.pony │ ├── print_match.pony │ ├── print_object.pony │ ├── print_operator.pony │ ├── print_postfix.pony │ ├── print_recover.pony │ ├── print_try.pony │ ├── print_type.pony │ ├── print_use.pony │ ├── print_with.pony │ ├── sugar_assign.pony │ ├── sugar_binary_op.pony │ ├── sugar_jump.pony │ ├── sugar_match.pony │ ├── sugar_type_decl.pony │ ├── sugar_unary_op.pony │ ├── syntax_ffi.pony │ ├── syntax_jump.pony │ ├── syntax_lambda.pony │ ├── syntax_match.pony │ ├── syntax_method.pony │ ├── syntax_subdir.pony │ ├── syntax_subdir │ │ └── main.pony │ ├── syntax_type.pony │ └── syntax_type_decl.pony ├── main.pony ├── test_command.pony ├── test_command_type.pony └── test_fixture.pony └── unreachable └── unreachable.pony /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | ponycc/ast/gen/gen 3 | ponycc/test/test 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2015, Joe Eli McIlvain. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PONYC ?= ponyc 2 | 3 | .PHONY: all test 4 | all: ponycc/test/test 5 | 6 | test: ponycc/test/test 7 | ponycc/test/test 8 | 9 | clean: 10 | rm -f ponycc/ast/gen/gen 11 | rm -f ponycc/ast/ast.pony 12 | rm -f ponycc/pass/parse/_parser.pony 13 | rm -f ponycc/test/test 14 | 15 | ponycc/ast/gen/gen: $(shell ls ponycc/ast/gen/*.pony) 16 | stable env $(PONYC) --debug -o ponycc/ast/gen ponycc/ast/gen 17 | 18 | ponycc/ast/ast.pony: ponycc/ast/gen/gen 19 | ponycc/ast/gen/gen ast > $@ 20 | 21 | ponycc/pass/parse/_parser.pony: ponycc/ast/gen/gen 22 | ponycc/ast/gen/gen parser > $@ 23 | 24 | ponycc/test/test: \ 25 | ponycc/ast/ast.pony \ 26 | ponycc/pass/parse/_parser.pony \ 27 | $(shell sh -c "find ponycc | grep .pony | grep -v fixtures") \ 28 | 29 | stable env $(PONYC) --debug -o ponycc/test ponycc/test 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ponycc 2 | 3 | Pony package for parsing, manipulating, and compiling Pony code. :horse: :horse: 4 | 5 | NOTE: This repo was formerly named `pony-ast` and was dedicated to parsing / AST only, but the scope has now been expanded to also include work on a Pony compiler. The `ast` subpackage is still isolated enough that it can be used independently without having to use the entire compiler. 6 | -------------------------------------------------------------------------------- /bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "deps": [ 3 | { 4 | "type": "github", 5 | "repo": "sylvanc/peg" 6 | }, 7 | { 8 | "type": "github", 9 | "repo": "jemc/pony-inspect" 10 | }, 11 | { 12 | "type": "github", 13 | "repo": "ponylang/glob" 14 | }, 15 | { 16 | "type": "github", 17 | "repo": "ponylang/regex" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /ponycc/ast/_ast_util.pony: -------------------------------------------------------------------------------- 1 | 2 | primitive _ASTUtil 3 | fun parse_lit_integer(pos: SourcePosAny): U128 ? => 4 | pos.string().u128()? 5 | 6 | fun parse_lit_float(pos: SourcePosAny): F64 ? => 7 | // TODO: fix stdlib String.f64 to error on failure. 8 | if pos.length() == 0 then error end 9 | pos.string().f64()? 10 | 11 | fun parse_lit_string(pos: SourcePosAny): String ? => 12 | let quotes_length = 13 | if pos.string().at("\"\"\"") 14 | then USize(3) 15 | else USize(1) 16 | end 17 | 18 | if pos.length() < (2 * quotes_length) then error end 19 | 20 | // TODO: handle escaped characters 21 | SourcePos(pos.source(), pos.offset() + quotes_length, pos.length() - (2 * quotes_length)).string() 22 | 23 | fun parse_lit_character(pos: SourcePosAny): U8 ? => 24 | // TODO: handle escaped characters 25 | if pos.length() != 3 then error end 26 | SourcePos(pos.source(), pos.offset() + 1, pos.length() - 2).string()(0)? 27 | 28 | fun parse_id(pos: SourcePosAny): String ? => 29 | if pos.length() == 0 then error end 30 | pos.string() 31 | -------------------------------------------------------------------------------- /ponycc/ast/attachments.pony: -------------------------------------------------------------------------------- 1 | 2 | use mut = "collections" 3 | use coll = "collections/persistent" 4 | 5 | trait val _AttachmentTagAny 6 | primitive _AttachmentTag[T] is _AttachmentTagAny 7 | 8 | class val Attachments 9 | let _val_map: coll.HashMap[_AttachmentTagAny, Any val, mut.HashIs[_AttachmentTagAny]] 10 | let _tag_map: coll.HashMap[_AttachmentTagAny, Any tag, mut.HashIs[_AttachmentTagAny]] 11 | 12 | new val create( 13 | val_map': coll.HashMap[_AttachmentTagAny, Any val, mut.HashIs[_AttachmentTagAny]] = coll.HashMap[_AttachmentTagAny, Any val, mut.HashIs[_AttachmentTagAny]], 14 | tag_map': coll.HashMap[_AttachmentTagAny, Any tag, mut.HashIs[_AttachmentTagAny]] = coll.HashMap[_AttachmentTagAny, Any tag, mut.HashIs[_AttachmentTagAny]]) 15 | => 16 | _val_map = val_map' 17 | _tag_map = tag_map' 18 | 19 | fun find_val[A: Any val](): A? => 20 | _val_map(_AttachmentTag[A])? as A 21 | 22 | fun find_tag[A: Any tag](): A? => 23 | _tag_map(_AttachmentTag[A])? as A 24 | 25 | fun val attach_val[A: Any val](a: A): Attachments => 26 | create(_val_map(_AttachmentTag[A]) = a, _tag_map) 27 | 28 | fun val attach_tag[A: Any tag](a: A): Attachments => 29 | create(_val_map, _tag_map(_AttachmentTag[A]) = a) 30 | -------------------------------------------------------------------------------- /ponycc/ast/gen/ast_defs.pony: -------------------------------------------------------------------------------- 1 | 2 | primitive ASTDefs 3 | fun apply(g: ASTGen) => 4 | g.def_actor("_Program") 5 | .> has_list("packages", "package", "Package") 6 | 7 | g.def_actor("Package") 8 | .> has_list("type_decls", "type_decl", "PackageTypeDecl") 9 | .> has_list("ffi_decls", "ffi_decl", "UseFFIDecl") 10 | .> has_list("docs", "doc", "LitString") 11 | 12 | g.def_actor("PackageTypeDecl") 13 | .> has_list("use_packages", "use_package", "UsePackage") 14 | .> has_var("type_decl", "TypeDecl") 15 | 16 | g.def("Module") 17 | .> with_scope() 18 | .> has("use_decls", "coll.Vec[UseDecl]", "coll.Vec[UseDecl]") 19 | .> has("type_decls", "coll.Vec[TypeDecl]", "coll.Vec[TypeDecl]") 20 | .> has("docs", "(LitString | None)", "None") 21 | 22 | g.def("UsePackage") 23 | .> in_union("UseDecl") 24 | .> has("prefix", "(Id | None)", "None") 25 | .> has("package", "LitString") 26 | 27 | g.def("UseFFIDecl") 28 | .> in_union("UseDecl") 29 | .> has("name", "(Id | LitString)") 30 | .> has("return_type", "TypeArgs") 31 | .> has("params", "Params") 32 | .> has("partial", "(Question | None)") 33 | .> has("guard", "(IfDefCond | None)", "None") 34 | 35 | for name in [ 36 | "TypeAlias"; "Interface"; "Trait"; "Primitive"; "Struct"; "Class"; "Actor" 37 | ].values() do 38 | g.def(name) 39 | .> in_union("TypeDecl") 40 | .> has("name", "Id") 41 | .> has("cap", "(Cap | None)", "None") 42 | .> has("type_params", "(TypeParams | None)", "None") 43 | .> has("provides", "(Type | None)", "None") 44 | .> has("members", "Members", "Members") 45 | .> has("at", "(At | None)", "None") 46 | .> has("docs", "(LitString | None)", "None") 47 | end 48 | 49 | g.def("Members") 50 | .> has("fields", "coll.Vec[Field]", "coll.Vec[Field]") 51 | .> has("methods", "coll.Vec[Method]", "coll.Vec[Method]") 52 | 53 | for name in ["FieldLet"; "FieldVar"; "FieldEmbed"].values() do 54 | g.def(name) 55 | .> in_union("Field") 56 | .> with_type() 57 | .> has("name", "Id") 58 | .> has("field_type", "Type") 59 | .> has("default", "(Expr | None)", "None") 60 | end 61 | 62 | for name in ["MethodFun"; "MethodNew"; "MethodBe"].values() do 63 | g.def(name) 64 | .> in_union("Method") 65 | .> with_scope() 66 | .> has("name", "Id") 67 | .> has("cap", "(Cap | At | None)", "None") 68 | .> has("type_params", "(TypeParams | None)", "None") 69 | .> has("params", "Params", "Params") 70 | .> has("return_type", "(Type | None)", "None") 71 | .> has("partial", "(Question | None)", "None") 72 | .> has("guard", "(Sequence | None)", "None") 73 | .> has("body", "(Sequence | None)", "None") 74 | .> has("docs", "(LitString | None)", "None") 75 | end 76 | 77 | g.def("TypeParams") 78 | .> has("list", "coll.Vec[TypeParam]", "coll.Vec[TypeParam]") 79 | 80 | g.def("TypeParam") 81 | .> has("name", "Id") 82 | .> has("constraint", "(Type | None)", "None") 83 | .> has("default", "(Type | None)", "None") 84 | 85 | g.def("TypeArgs") 86 | .> has("list", "coll.Vec[Type]", "coll.Vec[Type]") 87 | 88 | g.def("Params") 89 | .> has("list", "coll.Vec[Param]", "coll.Vec[Param]") // TODO: account for case where parser emits ellipsis in multiple argument positions 90 | .> has("ellipsis", "(Ellipsis | None)", "None") 91 | 92 | g.def("Param") 93 | .> with_type() 94 | .> has("name", "Id") 95 | .> has("param_type", "(Type | None)", "None") 96 | .> has("default", "(Expr | None)", "None") 97 | 98 | g.def("Sequence") 99 | .> with_scope() // TODO: doesn't always have a scope... 100 | .> in_union("Expr") 101 | .> has("list", "coll.Vec[Expr]", "coll.Vec[Expr]") 102 | 103 | for name in [ 104 | "Return"; "Break"; "Continue"; "Error"; "CompileIntrinsic"; "CompileError" 105 | ].values() do 106 | g.def(name) 107 | .> in_union("Jump", "Expr") 108 | .> has("value", "(Expr | None)", "None") 109 | end 110 | 111 | g.def("IfDefFlag") 112 | .> in_union("IfDefCond") 113 | .> has("name", "(Id | LitString)") 114 | 115 | g.def("IfDefNot") 116 | .> in_union("IfDefCond") 117 | .> has("expr", "IfDefCond") 118 | 119 | for name in ["IfDefAnd"; "IfDefOr"].values() do 120 | g.def(name) 121 | .> in_union("IfDefBinaryOp", "IfDefCond") 122 | .> has("left", "IfDefCond") 123 | .> has("right", "IfDefCond") 124 | end 125 | 126 | g.def("IfDef") 127 | .> in_union("Expr") 128 | .> with_scope() 129 | .> with_type() 130 | .> has("condition", "IfDefCond") 131 | .> has("then_body", "Sequence") 132 | .> has("else_body", "(Sequence | IfDef | None)", "None") 133 | 134 | g.def("IfType") 135 | .> in_union("Expr") 136 | .> with_scope() 137 | .> with_type() 138 | .> has("sub", "Type") 139 | .> has("super", "Type") 140 | .> has("then_body", "Sequence") 141 | .> has("else_body", "(Sequence | IfType | None)", "None") 142 | 143 | g.def("If") 144 | .> in_union("Expr") 145 | .> with_scope() 146 | .> with_type() 147 | .> has("condition", "Sequence") 148 | .> has("then_body", "Sequence") 149 | .> has("else_body", "(Sequence | If | None)", "None") 150 | 151 | g.def("While") 152 | .> in_union("Expr") 153 | .> with_scope() 154 | .> with_type() 155 | .> has("condition", "Sequence") 156 | .> has("loop_body", "Sequence") 157 | .> has("else_body", "(Sequence | None)", "None") 158 | 159 | g.def("Repeat") 160 | .> in_union("Expr") 161 | .> with_scope() 162 | .> with_type() 163 | .> has("loop_body", "Sequence") 164 | .> has("condition", "Sequence") 165 | .> has("else_body", "(Sequence | None)", "None") 166 | 167 | g.def("For") 168 | .> in_union("Expr") 169 | .> with_scope() 170 | .> with_type() 171 | .> has("refs", "(Id | IdTuple)") 172 | .> has("iterator", "Sequence") 173 | .> has("loop_body", "Sequence") 174 | .> has("else_body", "(Sequence | None)", "None") 175 | 176 | g.def("With") 177 | .> in_union("Expr") 178 | .> with_scope() 179 | .> with_type() 180 | .> has("assigns", "AssignTuple") 181 | .> has("body", "Sequence") 182 | .> has("else_body", "(Sequence | None)", "None") 183 | 184 | g.def("IdTuple") // TODO: implement as Tuple[(Id | IdTuple)] 185 | .> in_union("Expr") // TODO: remove? 186 | .> with_type() 187 | .> has("elements", "coll.Vec[(Id | IdTuple)]", "coll.Vec[(Id | IdTuple)]") 188 | 189 | g.def("AssignTuple") // TODO: implement as Tuple[Assign] 190 | .> with_type() 191 | .> has("elements", "coll.Vec[Assign]", "coll.Vec[Assign]") 192 | 193 | g.def("Match") 194 | .> in_union("Expr") 195 | .> with_scope() 196 | .> with_type() 197 | .> has("expr", "Sequence") 198 | .> has("cases", "Cases", "Cases") 199 | .> has("else_body", "(Sequence | None)", "None") 200 | 201 | g.def("Cases") 202 | .> with_scope() // to simplify branch consolidation 203 | .> with_type() 204 | .> has("list", "coll.Vec[Case]", "coll.Vec[Case]") 205 | 206 | g.def("Case") 207 | .> with_scope() 208 | .> has("expr", "Expr") 209 | .> has("guard", "(Sequence | None)", "None") 210 | .> has("body", "(Sequence | None)", "None") 211 | 212 | g.def("Try") 213 | .> in_union("Expr") 214 | .> with_type() 215 | .> has("body", "Sequence") 216 | .> has("else_body", "(Sequence | None)", "None") 217 | .> has("then_body", "(Sequence | None)", "None") 218 | 219 | g.def("Consume") 220 | .> in_union("Expr") 221 | .> with_type() 222 | .> has("cap", "(Cap | None)") 223 | .> has("expr", "(Reference | This)") 224 | 225 | g.def("Recover") 226 | .> in_union("Expr") 227 | .> with_type() 228 | .> has("cap", "(Cap | None)") 229 | .> has("expr", "Sequence") 230 | 231 | g.def("As") 232 | .> in_union("Expr") 233 | .> with_type() 234 | .> has("expr", "Expr") 235 | .> has("as_type", "Type") 236 | 237 | for name in [ 238 | "Add"; "AddUnsafe"; "Sub"; "SubUnsafe" 239 | "Mul"; "MulUnsafe"; "Div"; "DivUnsafe"; "Mod"; "ModUnsafe" 240 | "LShift"; "LShiftUnsafe"; "RShift"; "RShiftUnsafe" 241 | "Eq"; "EqUnsafe"; "NE"; "NEUnsafe" 242 | "LT"; "LTUnsafe"; "LE"; "LEUnsafe" 243 | "GE"; "GEUnsafe"; "GT"; "GTUnsafe" 244 | "Is"; "Isnt"; "And"; "Or"; "XOr" 245 | ].values() do 246 | g.def(name) 247 | .> in_union("BinaryOp", "Expr") 248 | .> with_type() 249 | .> has("left", "Expr") 250 | .> has("right", "Expr") 251 | .> has("partial", "(Question | None)", "None") 252 | end 253 | 254 | for name in [ 255 | "Not"; "Neg"; "NegUnsafe"; "AddressOf"; "DigestOf" 256 | ].values() do 257 | g.def(name) 258 | .> in_union("UnaryOp", "Expr") 259 | .> with_type() 260 | .> has("expr", "Expr") 261 | end 262 | 263 | for name in ["LocalLet"; "LocalVar"].values() do 264 | g.def(name) 265 | .> in_union("Local", "Expr") 266 | .> with_type() 267 | .> has("name", "Id") 268 | .> has("local_type", "(Type | None)", "None") 269 | end 270 | 271 | g.def("Assign") 272 | .> in_union("Expr") 273 | .> with_type() 274 | .> has("left", "Expr") 275 | .> has("right", "Expr") 276 | 277 | g.def("Dot") 278 | .> in_union("Expr") 279 | .> with_type() 280 | .> has("left", "Expr") 281 | .> has("right", "Id") 282 | 283 | g.def("Chain") 284 | .> in_union("Expr") 285 | .> with_type() 286 | .> has("left", "Expr") 287 | .> has("right", "Id") 288 | 289 | g.def("Tilde") 290 | .> in_union("Expr") 291 | .> with_type() 292 | .> has("left", "Expr") 293 | .> has("right", "Id") 294 | 295 | g.def("Qualify") 296 | .> in_union("Expr") 297 | .> has("left", "Expr") 298 | .> has("right", "TypeArgs") 299 | 300 | g.def("Call") 301 | .> in_union("Expr") 302 | .> with_type() 303 | .> has("callable", "Expr") 304 | .> has("args", "Args", "Args") 305 | .> has("named_args", "NamedArgs", "NamedArgs") 306 | .> has("partial", "(Question | None)", "None") 307 | 308 | g.def("CallFFI") 309 | .> in_union("Expr") 310 | .> with_type() 311 | .> has("name", "(Id | LitString)") 312 | .> has("type_args", "(TypeArgs | None)", "None") 313 | .> has("args", "Args", "Args") 314 | .> has("named_args", "NamedArgs", "NamedArgs") 315 | .> has("partial", "(Question | None)", "None") 316 | 317 | g.def("Args") 318 | .> has("list", "coll.Vec[Sequence]", "coll.Vec[Sequence]") 319 | 320 | g.def("NamedArgs") 321 | .> has("list", "coll.Vec[NamedArg]", "coll.Vec[NamedArg]") 322 | 323 | g.def("NamedArg") 324 | .> has("name", "Id") 325 | .> has("value", "Sequence") 326 | 327 | g.def("Lambda") 328 | .> in_union("Expr") 329 | .> with_type() 330 | .> has("method_cap", "(Cap | None)", "None") 331 | .> has("name", "(Id | None)", "None") 332 | .> has("type_params", "(TypeParams | None)", "None") 333 | .> has("params", "Params", "Params") 334 | .> has("captures", "(LambdaCaptures | None)", "None") 335 | .> has("return_type", "(Type | None)", "None") 336 | .> has("partial", "(Question | None)", "None") 337 | .> has("body", "Sequence", "Sequence") 338 | .> has("object_cap", "(Cap | None)", "None") 339 | 340 | g.def("BareLambda") 341 | .> in_union("Expr") 342 | .> with_type() 343 | .> has("method_cap", "(Cap | None)", "None") 344 | .> has("name", "(Id | None)", "None") 345 | .> has("type_params", "(TypeParams | None)", "None") 346 | .> has("params", "Params", "Params") 347 | .> has("captures", "(LambdaCaptures | None)", "None") 348 | .> has("return_type", "(Type | None)", "None") 349 | .> has("partial", "(Question | None)", "None") 350 | .> has("body", "Sequence", "Sequence") 351 | .> has("object_cap", "(Cap | None)", "None") 352 | 353 | g.def("LambdaCaptures") 354 | .> has("list", "coll.Vec[LambdaCapture]", "coll.Vec[LambdaCapture]") 355 | 356 | g.def("LambdaCapture") 357 | .> has("name", "Id") 358 | .> has("local_type", "(Type | None)", "None") 359 | .> has("value", "(Expr | None)", "None") 360 | 361 | g.def("Object") 362 | .> in_union("Expr") 363 | .> has("cap", "(Cap | None)", "None") 364 | .> has("provides", "(Type | None)", "None") 365 | .> has("members", "(Members | None)", "None") 366 | 367 | g.def("LitArray") 368 | .> in_union("Expr") 369 | .> has("elem_type", "(Type | None)", "None") 370 | .> has("sequence", "Sequence", "Sequence") 371 | 372 | g.def("Tuple") 373 | .> in_union("Expr") 374 | .> with_type() 375 | .> has("elements", "coll.Vec[Sequence]", "coll.Vec[Sequence]") 376 | 377 | g.def("This") 378 | .> in_union("Expr") 379 | 380 | for name in ["LitTrue"; "LitFalse"].values() do 381 | g.def(name) 382 | .> in_union("LitBool", "Expr") 383 | end 384 | 385 | g.def_wrap("LitInteger", "U128", "_ASTUtil.parse_lit_integer") 386 | .> in_union("Expr") 387 | 388 | g.def_wrap("LitFloat", "F64", "_ASTUtil.parse_lit_float") 389 | .> in_union("Expr") 390 | 391 | // TODO: Distinguish between LitString and LitStringTriple 392 | g.def_wrap("LitString", "String", "_ASTUtil.parse_lit_string") 393 | .> in_union("Expr") 394 | 395 | g.def_wrap("LitCharacter", "U8", "_ASTUtil.parse_lit_character") 396 | .> in_union("Expr") 397 | 398 | g.def("LitLocation") 399 | .> in_union("Expr") 400 | .> with_type() 401 | 402 | g.def("Reference") 403 | .> with_type() 404 | .> has("name", "Id") 405 | .> in_union("Expr") 406 | 407 | g.def("DontCare") 408 | .> with_type() 409 | .> in_union("Expr") 410 | 411 | g.def("PackageRef") 412 | .> has("name", "Id") 413 | .> in_union("Expr") 414 | 415 | for name in ["MethodFunRef"; "MethodNewRef"; "MethodBeRef"].values() do 416 | g.def(name) 417 | .> in_union("MethodRef", "Expr") 418 | .> with_scope() 419 | .> has("receiver", "Expr") 420 | .> has("name", "(Id | TypeArgs)") // TODO: don't use this weird scheme 421 | end 422 | 423 | g.def("TypeRef") 424 | .> in_union("Expr") 425 | .> with_type() 426 | .> has("package", "Expr") 427 | .> has("name", "(Id | TypeArgs)") // TODO: don't use this weird scheme 428 | 429 | for name in ["FieldLetRef"; "FieldVarRef"; "FieldEmbedRef"].values() do 430 | g.def(name) 431 | .> in_union("FieldRef", "Expr") 432 | .> with_type() 433 | .> has("receiver", "Expr") 434 | .> has("name", "Id") 435 | end 436 | 437 | g.def("TupleElementRef") 438 | .> in_union("Expr") 439 | .> with_type() 440 | .> has("receiver", "Expr") 441 | .> has("name", "LitInteger") 442 | 443 | for name in ["LocalLetRef"; "LocalVarRef"; "ParamRef"].values() do 444 | g.def(name) 445 | .> in_union("LocalRef", "Expr") 446 | .> with_type() 447 | .> has("name", "Id") 448 | end 449 | 450 | g.def("ViewpointType") 451 | .> in_union("Type") 452 | .> has("left", "Type") 453 | .> has("right", "Type") 454 | 455 | g.def("UnionType") 456 | .> in_union("Type") 457 | .> has("list", "coll.Vec[Type]", "coll.Vec[Type]") // TODO: try to fix the parser compat with this, using seq() instead of _BuildInfix, or at least flattening in the parser first (maybe _BuildInfixFlat?) 458 | 459 | g.def("IsectType") 460 | .> in_union("Type") 461 | .> has("list", "coll.Vec[Type]", "coll.Vec[Type]") // TODO: try to fix the parser compat with this, using seq() instead of _BuildInfix, or at least flattening in the parser first (maybe _BuildInfixFlat?) 462 | 463 | g.def("TupleType") 464 | .> in_union("Type") 465 | .> has("list", "coll.Vec[Type]", "coll.Vec[Type]") // TODO: confirm parser compat with this 466 | 467 | g.def("NominalType") 468 | .> in_union("Type") 469 | .> has("name", "Id") 470 | .> has("package", "(Id | None)", "None") 471 | .> has("type_args", "(TypeArgs | None)", "None") 472 | .> has("cap", "(Cap | GenCap | None)", "None") 473 | .> has("cap_mod", "(CapMod | None)", "None") 474 | 475 | g.def("FunType") 476 | .> in_union("Type") 477 | .> has("cap", "Cap") 478 | .> has("type_params", "(TypeParams | None)", "None") 479 | .> has("params", "Params", "Params") 480 | .> has("return_type", "(Type | None)", "None") 481 | 482 | g.def("LambdaType") 483 | .> in_union("Type") 484 | .> has("method_cap", "(Cap | None)", "None") 485 | .> has("name", "(Id | None)", "None") 486 | .> has("type_params", "(TypeParams | None)", "None") 487 | .> has("param_types", "TupleType", "TupleType") 488 | .> has("return_type", "(Type | None)", "None") 489 | .> has("partial", "(Question | None)", "None") 490 | .> has("object_cap", "(Cap | GenCap | None)", "None") 491 | .> has("cap_mod", "(CapMod | None)", "None") 492 | 493 | g.def("BareLambdaType") 494 | .> in_union("Type") 495 | .> has("method_cap", "(Cap | None)", "None") 496 | .> has("name", "(Id | None)", "None") 497 | .> has("type_params", "(TypeParams | None)", "None") 498 | .> has("param_types", "TupleType", "TupleType") 499 | .> has("return_type", "(Type | None)", "None") 500 | .> has("partial", "(Question | None)", "None") 501 | .> has("object_cap", "(Cap | GenCap | None)", "None") 502 | .> has("cap_mod", "(CapMod | None)", "None") 503 | 504 | g.def("TypeParamRef") 505 | .> in_union("Type") 506 | .> has("name", "Id") 507 | .> has("cap", "(Cap | GenCap | None)", "None") 508 | .> has("cap_mod", "(CapMod | None)", "None") 509 | 510 | g.def("ThisType") 511 | .> in_union("Type") 512 | 513 | g.def("DontCareType") 514 | .> in_union("Type") 515 | 516 | g.def("ErrorType") 517 | .> in_union("Type") 518 | 519 | g.def("LiteralType") 520 | .> in_union("Type") 521 | 522 | g.def("LiteralTypeBranch") 523 | .> in_union("Type") 524 | 525 | g.def("OpLiteralType") 526 | .> in_union("Type") 527 | 528 | for name in ["Iso"; "Trn"; "Ref"; "Val"; "Box"; "Tag"].values() do 529 | g.def(name) 530 | .> in_union("Cap", "Type") 531 | end 532 | 533 | for name in [ 534 | "CapRead"; "CapSend"; "CapShare"; "CapAlias"; "CapAny" 535 | ].values() do 536 | g.def(name) 537 | .> in_union("GenCap", "Type") 538 | end 539 | 540 | for name in ["Aliased"; "Ephemeral"].values() do 541 | g.def(name) 542 | .> in_union("CapMod") 543 | end 544 | 545 | g.def("At") 546 | 547 | g.def("Question") 548 | 549 | g.def("Ellipsis") 550 | 551 | g.def("Annotation") 552 | 553 | g.def("Semicolon") 554 | .> in_union("Expr") // only so we can nicely error for semicolon at EOL. 555 | .> has("list", "coll.Vec[Expr]") 556 | 557 | g.def_wrap("Id", "String", "_ASTUtil.parse_id") 558 | .> in_union("Expr") // TODO: remove? 559 | 560 | for name in [ 561 | "EOF" 562 | "NewLine" 563 | "Use" 564 | "Colon" 565 | "Comma" 566 | "Constant" 567 | "Pipe" 568 | "Ampersand" 569 | "SubType" 570 | "Arrow" 571 | "DoubleArrow" 572 | "AtLBrace" 573 | "LBrace" 574 | "RBrace" 575 | "LParen" 576 | "RParen" 577 | "LSquare" 578 | "RSquare" 579 | "LParenNew" 580 | "LBraceNew" 581 | "LSquareNew" 582 | "SubNew" 583 | "SubUnsafeNew" 584 | "In" 585 | "Until" 586 | "Do" 587 | "Else" 588 | "ElseIf" 589 | "Then" 590 | "End" 591 | "Var" 592 | "Let" 593 | "Embed" 594 | "Where" 595 | ].values() do 596 | g.def_lexeme(name) 597 | .> in_union("Lexeme") 598 | end 599 | -------------------------------------------------------------------------------- /ponycc/ast/gen/ast_gen.pony: -------------------------------------------------------------------------------- 1 | 2 | use "collections" 3 | 4 | class ASTGen 5 | let defs: List[ASTGenDef] = defs.create() 6 | let unions: Map[String, SetIs[ASTGenDef]] = unions.create() 7 | 8 | new ref create() => None 9 | 10 | fun ref def(n: String): ASTGenDefFixed => 11 | ASTGenDefFixed(this, n) 12 | 13 | fun ref def_wrap(n: String, t: String, p: String): ASTGenDefWrap => 14 | ASTGenDefWrap(this, n, t, p) 15 | 16 | fun ref def_lexeme(n: String): ASTGenDefLexeme => 17 | ASTGenDefLexeme(this, n) 18 | 19 | fun ref def_actor(n: String): ASTGenDefActor => 20 | ASTGenDefActor(this, n) 21 | 22 | fun string(): String => 23 | let g: CodeGen = CodeGen 24 | 25 | // Use some persistent collections. 26 | g.line("use coll = \"collections/persistent\"") 27 | g.line() 28 | 29 | // Declare the AST trait 30 | g.line("trait val AST") 31 | g.push_indent() 32 | g.line("fun val size(): USize") 33 | g.line("fun val apply(idx: USize): (AST | None)?") 34 | g.line("fun val values(): Iterator[(AST | None)]^ =>") 35 | g.line(" object is Iterator[(AST | None)]") 36 | g.line(" let ast: AST = this") 37 | g.line(" var idx: USize = 0") 38 | g.line(" fun has_next(): Bool => idx < ast.size()") 39 | g.line(" fun ref next(): (AST | None)? => ast(idx = idx + 1)?") 40 | g.line(" end") 41 | g.line("fun val pairs(): Iterator[(USize, (AST | None))]^ =>") 42 | g.line(" object is Iterator[(USize, (AST | None))]") 43 | g.line(" let ast: AST = this") 44 | g.line(" var idx: USize = 0") 45 | g.line(" fun has_next(): Bool => idx < ast.size()") 46 | g.line(" fun ref next(): (USize, (AST | None))? => (idx, ast(idx = idx + 1)?)") 47 | g.line(" end") 48 | g.line("fun val attach_val[A: Any val](a: A): AST") 49 | g.line("fun val attach_tag[A: Any tag](a: A): AST") 50 | g.line("fun val find_attached_val[A: Any val](): A?") 51 | g.line("fun val find_attached_tag[A: Any tag](): A?") 52 | g.line("fun val apply_specialised[C](c: C, fn: {[A: AST val](C, A)} val)") 53 | g.line("fun val get_child_dynamic(child': String, index': USize = 0): (AST | None)?") 54 | g.line("fun val with_replaced_child(child': AST, replace': (AST | None)): AST") 55 | g.line("fun val pos(): SourcePosAny") 56 | g.line("fun string(): String iso^") 57 | g.block( 58 | """ 59 | new from_iter( 60 | iter: Iterator[(AST | None)], 61 | pos': SourcePosAny = SourcePosNone, 62 | errs: Array[(String, SourcePosAny)] = [])? 63 | """) 64 | g.pop_indent() 65 | g.line() 66 | 67 | // Declare the ASTInfo primitive 68 | g.line("primitive ASTInfo") 69 | g.push_indent() 70 | g.line("fun name[A: (AST | None)](): String =>") 71 | g.push_indent() 72 | g.line("iftype A <: None then \"x\"") 73 | for d in defs.values() do 74 | g.line("elseif A <: " + d.name() + " then \"" + d.name() + "\"") 75 | end 76 | g.line("else \"???\"") 77 | g.line("end") 78 | g.pop_indent() 79 | g.pop_indent() 80 | g.line() 81 | 82 | // Declare each type union. 83 | for (name, union_defs) in unions.pairs() do 84 | // g.line("type " + name + " is (") 85 | // let iter = union_defs.values() 86 | // for t in iter do 87 | // g.add(t) 88 | // if iter.has_next() then g.add(" | ") end 89 | // end 90 | // g.add(")") 91 | 92 | g.line("trait val " + name + " is AST") // TODO: use union instead 93 | g.push_indent() 94 | 95 | // Determine which fields are common among all types. 96 | let common_fields = Map[String, String] 97 | let union_iter = union_defs.values() 98 | for union_def in union_iter do 99 | match union_def | let def_fixed: ASTGenDefFixed box => 100 | for (field_name, field_type, _) in def_fixed.fields.values() do 101 | common_fields(field_name) = field_type 102 | end 103 | break // stop after processing just the first ASTGenDefFixed 104 | end 105 | end 106 | for union_def in union_iter do 107 | match union_def | let def_fixed: ASTGenDefFixed box => 108 | // This snapshot is needed because we can't remove elements from the 109 | // map while iterating over it - the iterator will bug out. 110 | let common_fields_snapshot = 111 | Array[(String, String)].>concat(common_fields.pairs()) 112 | 113 | for (field_name, field_type) in common_fields_snapshot.values() do 114 | if not 115 | def_fixed.fields.contains((field_name, field_type, ""), 116 | {(l, r) => (l._1 == r._1) and (l._2 == r._2) }) 117 | then 118 | try common_fields.remove(field_name)? end 119 | end 120 | end 121 | end 122 | end 123 | 124 | // Define getter and setter trait methods for all common fields. 125 | for (field_name, field_type) in common_fields.pairs() do 126 | g.line("fun val " + field_name + "(): " + field_type) 127 | end 128 | for (field_name, field_type) in common_fields.pairs() do 129 | g.line("fun val with_" + field_name + "(") 130 | g.add(field_name + "': " + field_type + "): " + name) 131 | end 132 | 133 | g.pop_indent() 134 | g.line() 135 | end 136 | 137 | // Declare each class def. 138 | for d in defs.values() do 139 | d.code_gen(g) 140 | end 141 | 142 | g.string() 143 | 144 | fun ref _add_to_union(u: String, d: ASTGenDef) => 145 | try unions(u)?.set(d) 146 | else unions(u) = SetIs[ASTGenDef].>set(d) 147 | end 148 | -------------------------------------------------------------------------------- /ponycc/ast/gen/ast_gen_def.pony: -------------------------------------------------------------------------------- 1 | 2 | trait ASTGenDef 3 | fun name(): String 4 | fun code_gen(g: CodeGen) 5 | 6 | class ASTGenDefFixed is ASTGenDef 7 | let _gen: ASTGen 8 | let _name: String 9 | let _traits: Array[String] = [] // TODO: remove and use unions only 10 | 11 | let fields: Array[(String, String, String)] = [] 12 | 13 | var _todo: Bool = false 14 | var _with_scope: Bool = false 15 | var _with_type: Bool = false 16 | 17 | new create(g: ASTGen, n: String) => 18 | (_gen, _name) = (g, n) 19 | _gen.defs.push(this) 20 | 21 | fun ref has(n: String, t: String, d: String = "") => fields.push((n, t, d)) 22 | 23 | fun ref todo() => _todo = true 24 | fun ref with_scope() => _with_scope = true 25 | fun ref with_type() => _with_type = true 26 | 27 | fun ref in_union(n: String, n2: String = "") => 28 | _traits.push(n) 29 | if n2.size() > 0 then _traits.push(n2) end 30 | _gen._add_to_union(n, this) 31 | if n2.size() > 0 then _gen._add_to_union(n2, this) end 32 | 33 | fun name(): String => _name 34 | 35 | fun code_gen(g: CodeGen) => 36 | g.line("class val " + _name + " is ") 37 | if _traits.size() == 0 then 38 | g.add("AST") 39 | else 40 | g.add("(AST") 41 | for t in _traits.values() do g.add(" & " + t) end 42 | g.add(")") 43 | end 44 | if _todo then g.add(" // TODO") end 45 | g.push_indent() 46 | 47 | // Declare common fields. 48 | g.line("let _attachments: (Attachments | None)") 49 | g.line() 50 | 51 | // Declare all fields. 52 | for (field_name, field_type, _) in fields.values() do 53 | g.line("let _" + field_name + ": " + field_type) 54 | end 55 | if fields.size() > 0 then g.line() end 56 | 57 | // Declare a constructor that initializes all fields from parameters. 58 | g.line("new val create(") 59 | var iter = fields.values() 60 | g.push_indent() 61 | for (field_name, field_type, field_default) in iter do 62 | g.line(field_name + "': ") 63 | if field_type.at("coll.Vec[") then 64 | let elem_type: String = field_type.substring(9, -1) 65 | g.add("(" + field_type + " | Array[" + elem_type + "] val)") 66 | else 67 | g.add(field_type) 68 | end 69 | if field_default.size() > 0 then g.add(" = " + field_default) end 70 | g.add(",") 71 | end 72 | g.line("attachments': (Attachments | None) = None)") 73 | g.pop_indent() 74 | g.line("=>") 75 | g.push_indent() 76 | g.add("_attachments = attachments'") 77 | for (field_name, field_type, _) in fields.values() do 78 | g.line("_" + field_name + " = ") 79 | if field_type.at("coll.Vec[") then 80 | let elem_type: String = field_type.substring(9, -1) 81 | g.push_indent() 82 | g.line("match " + field_name + "'") 83 | g.line("| let v: coll.Vec[" + elem_type + "] => v") 84 | g.line("| let s: Array[" + elem_type + "] val => ") 85 | g.add("coll.Vec[" + elem_type + "].concat(s.values())") 86 | g.line("end") 87 | g.pop_indent() 88 | else 89 | g.add(field_name + "'") 90 | end 91 | end 92 | g.pop_indent() 93 | g.line() 94 | 95 | // Declare a constructor that initializes all fields from an iterator. 96 | g.block( 97 | """ 98 | new from_iter( 99 | iter: Iterator[(AST | None)], 100 | pos': SourcePosAny = SourcePosNone, 101 | errs: Array[(String, SourcePosAny)] = [])? 102 | =>""") 103 | g.push_indent() 104 | g.line("_attachments = Attachments.attach_val[SourcePosAny](pos')") 105 | 106 | var iter_next = "iter.next()?" 107 | for (field_name, field_type, field_default) in fields.values() do 108 | if field_type.at("coll.Vec[") then 109 | let elem_type: String = field_type.substring(9, -1) 110 | g.line("var " + field_name + "' = " + field_type) 111 | g.line("var " + field_name + "_next' = ") 112 | if iter_next != "iter.next()?" then 113 | g.add(iter_next) 114 | else 115 | g.add("try iter.next()? else None end") 116 | end 117 | g.line("while true do") 118 | g.push_indent() 119 | g.line("try " + field_name + "' = ") 120 | g.add(field_name + "'.push(" + field_name + "_next'") 121 | g.add(" as " + elem_type + ") else break end") 122 | g.line("try " + field_name + "_next' = iter.next()?") 123 | g.add(" else " + field_name + "_next' = None; break end") 124 | g.pop_indent() 125 | g.line("end") 126 | iter_next = field_name + "_next'" 127 | else 128 | g.line("let " + field_name + "': (AST | None) =") 129 | if iter_next != "iter.next()?" then 130 | g.add(" " + iter_next) 131 | else 132 | if field_default.size() > 0 then 133 | g.add(" try iter.next()? else " + field_default + " end") 134 | else 135 | g.push_indent() 136 | g.line("try iter.next()?") 137 | g.line("else errs.push((\"" + _name + " missing required field: " + field_name + "\", pos')); error") 138 | g.line("end") 139 | g.pop_indent() 140 | end 141 | end 142 | iter_next = "iter.next()?" 143 | end 144 | end 145 | 146 | if iter_next != "iter.next()?" then 147 | g.line("if " + iter_next + " isnt None then") 148 | g.push_indent() 149 | g.line("let extra' = " + iter_next) 150 | g.line("errs.push((\"" + _name + " got unexpected extra field: \" + extra'.string(), try (extra' as AST).pos() else SourcePosNone end)); error") 151 | g.pop_indent() 152 | g.line("end") 153 | else 154 | g.line("if") 155 | g.push_indent() 156 | g.line("try") 157 | g.push_indent() 158 | g.line("let extra' = " + iter_next) 159 | g.line("errs.push((\"" + _name + " got unexpected extra field: \" + extra'.string(), try (extra' as AST).pos() else SourcePosNone end)); true") 160 | g.pop_indent() 161 | g.line("else false") 162 | g.line("end") 163 | g.pop_indent() 164 | g.line("then error end") 165 | end 166 | 167 | if fields.size() > 0 then g.line() end 168 | for (field_name, field_type, field_default) in fields.values() do 169 | g.line("_" + field_name + " =") 170 | if field_type.at("coll.Vec[") then 171 | g.add(" " + field_name + "'") 172 | else 173 | g.push_indent() 174 | g.line("try " + field_name + "' as " + field_type) 175 | g.line("else errs.push((\"" + _name + " got incompatible field: " + field_name) 176 | g.add("\", try (" + field_name + "' as AST).pos() else SourcePosNone end)); error") 177 | g.line("end") 178 | g.pop_indent() 179 | end 180 | end 181 | 182 | g.pop_indent() 183 | g.line() 184 | 185 | // Declare common helpers. 186 | g.line("fun val apply_specialised[C](c: C, fn: {[A: AST val](C, A)} val) => fn[" + _name + "](consume c, this)") 187 | 188 | // Declare common getters and setters. 189 | g.line("fun val pos(): SourcePosAny") 190 | g.add(" => try find_attached_val[SourcePosAny]()? else SourcePosNone end") 191 | g.line("fun val with_pos(pos': SourcePosAny): " + _name) 192 | g.add(" => attach_val[SourcePosAny](pos')") 193 | g.line() 194 | 195 | g.line("fun val size(): USize => ") 196 | for (idx, (field_name, field_type, _)) in fields.pairs() do 197 | if idx > 0 then g.add(" + ") end 198 | if field_type.at("coll.Vec[") then 199 | g.add("_" + field_name + ".size()") 200 | else 201 | g.add("1") 202 | end 203 | else 204 | g.add("0") 205 | end 206 | 207 | g.line("fun val apply(idx: USize): (AST | None)? =>") 208 | g.push_indent() 209 | g.line("var offset: USize = 0") 210 | for (idx, (field_name, field_type, _)) in fields.pairs() do 211 | if field_type.at("coll.Vec[") then 212 | g.line("if idx < (" + idx.string() + " + offset") 213 | g.add(" + _" + field_name + ".size())") 214 | g.add(" then return _" + field_name) 215 | g.add("(idx - (" + idx.string() + " + offset))? end") 216 | g.line("offset = (offset + _" + field_name + ".size()) - 1") 217 | else 218 | g.line("if idx == (" + idx.string() + " + offset)") 219 | g.add(" then return _" + field_name + " end") 220 | end 221 | end 222 | g.line("error") 223 | g.pop_indent() 224 | 225 | g.line("fun val attach_val[A: Any val](a: A): " + _name) 226 | g.add(" => create(") 227 | for (field_name, _, _) in fields.values() do 228 | g.add("_" + field_name + ", ") 229 | end 230 | g.add("(try _attachments as Attachments else Attachments end).attach_val[A](a))") 231 | g.line() 232 | 233 | g.line("fun val attach_tag[A: Any tag](a: A): " + _name) 234 | g.add(" => create(") 235 | for (field_name, _, _) in fields.values() do 236 | g.add("_" + field_name + ", ") 237 | end 238 | g.add("(try _attachments as Attachments else Attachments end).attach_tag[A](a))") 239 | g.line() 240 | 241 | g.line("fun val find_attached_val[A: Any val](): A? => (_attachments as Attachments).find_val[A]()?") 242 | 243 | g.line("fun val find_attached_tag[A: Any tag](): A? => (_attachments as Attachments).find_tag[A]()?") 244 | 245 | // Declare getter methods for all fields. 246 | for (field_name, field_type, _) in fields.values() do 247 | g.line("fun val " + field_name + "(): " + field_type + " => ") 248 | g.add("_" + field_name) 249 | end 250 | if fields.size() > 0 then g.line() end 251 | 252 | // Declare setter methods for all fields. 253 | for (field_name, field_type, _) in fields.values() do 254 | g.line("fun val with_" + field_name + "(") 255 | g.add(field_name + "': ") 256 | if field_type.at("coll.Vec[") then 257 | let elem_type: String = field_type.substring(9, -1) 258 | g.add("(" + field_type + " | Array[" + elem_type + "] val)") 259 | else 260 | g.add(field_type) 261 | end 262 | g.add("): " + _name) 263 | g.add(" => create(") 264 | for (other_field_name, _, _) in fields.values() do 265 | if other_field_name == field_name then 266 | g.add(field_name + "', ") 267 | else 268 | g.add("_" + other_field_name + ", ") 269 | end 270 | end 271 | g.add("_attachments)") 272 | end 273 | if fields.size() > 0 then g.line() end 274 | 275 | // Declare push methods for list fields. 276 | for (field_name, field_type, _) in fields.values() do 277 | if field_type.at("coll.Vec[", 0) then 278 | let elem_type: String = field_type.substring(9, -1) 279 | 280 | g.line("fun val with_" + field_name + "_push(") 281 | g.add("x: val->" + elem_type + "): " + _name) 282 | g.add(" => with_" + field_name) 283 | g.add("(_" + field_name + ".push(x))") 284 | g.line() 285 | end 286 | end 287 | 288 | // Declare a method that supports lookup of children by String name and 289 | // optional USize index number (only supported by coll.Vec fields). 290 | // Any invalid lookup is an error. 291 | g.line("fun val get_child_dynamic(child': String, index': USize = 0): (AST | None)? =>") 292 | if fields.size() == 0 then 293 | g.add(" error") 294 | else 295 | g.push_indent() 296 | g.line("match child'") 297 | for (field_name, field_type, _) in fields.values() do 298 | g.line("| \"" + field_name + "\" => _" + field_name) 299 | if field_type.at("coll.Vec[", 0) then 300 | g.add("(index')?") 301 | end 302 | end 303 | g.line("else error") 304 | g.line("end") 305 | g.pop_indent() 306 | end 307 | g.line() 308 | 309 | // Declare a method that finds (first occurrence of) the given child and 310 | // replaces it with the given new child, returning the resulting AST. 311 | // If the given child was not found, the original AST is returned. 312 | // If the given child is an incompatible type, the original AST is returned. 313 | // TODO: use a while loop to replace more than just the first occurrence. 314 | g.line("fun val with_replaced_child(child': AST, replace': (AST | None)): AST =>") 315 | g.push_indent() 316 | g.line("if child' is replace' then return this end") 317 | g.line("try") 318 | g.push_indent() 319 | for (field_name, field_type, _) in fields.values() do 320 | if field_type.at("coll.Vec[", 0) then 321 | let elem_type: String = field_type.substring(9, -1) 322 | g.line("try") 323 | g.push_indent() 324 | g.line("let i = _" + field_name + ".find(child' as " + elem_type + ")?") 325 | g.line("return create(") 326 | for (other_field_name, _, _) in fields.values() do 327 | if other_field_name == field_name then 328 | g.add("_" + field_name + ".update(i, replace' as " + elem_type + ")?, ") 329 | else 330 | g.add("_" + other_field_name + ", ") 331 | end 332 | end 333 | g.add("_attachments)") 334 | g.pop_indent() 335 | g.line("end") 336 | else 337 | g.line("if child' is _" + field_name + " then") 338 | g.push_indent() 339 | g.line("return create(") 340 | for (other_field_name, _, _) in fields.values() do 341 | if other_field_name == field_name then 342 | g.add("replace' as " + field_type + ", ") 343 | else 344 | g.add("_" + other_field_name + ", ") 345 | end 346 | end 347 | g.add("_attachments)") 348 | g.pop_indent() 349 | g.line("end") 350 | end 351 | end 352 | g.line("error") 353 | g.pop_indent() 354 | g.line("else this") 355 | g.line("end") 356 | g.pop_indent() 357 | g.line() 358 | 359 | // Declare a string method to print itself. 360 | g.line("fun string(): String iso^ =>") 361 | g.push_indent() 362 | g.line("let s = recover iso String end") 363 | g.line("s.append(\"" + _name + "\")") 364 | if fields.size() > 0 then 365 | g.line("s.push('(')") 366 | iter = fields.values() 367 | for (field_name, field_type, _) in iter do 368 | if field_type.at("coll.Vec[", 0) then 369 | g.line("s.push('[')") 370 | g.line("for (i, v) in _" + field_name + ".pairs() do") 371 | g.push_indent() 372 | g.line("if i > 0 then s.>push(';').push(' ') end") 373 | g.line("s.append(v.string())") 374 | g.pop_indent() 375 | g.line("end") 376 | g.line("s.push(']')") 377 | if iter.has_next() then g.line("s.>push(',').push(' ')") end 378 | else 379 | g.line("s.>append(_" + field_name + ".string())") 380 | if iter.has_next() then g.add(".>push(',').push(' ')") end 381 | end 382 | end 383 | g.line("s.push(')')") 384 | end 385 | g.line("consume s") 386 | g.pop_indent() 387 | 388 | g.pop_indent() 389 | g.line() 390 | 391 | class ASTGenDefWrap is ASTGenDef 392 | let _gen: ASTGen 393 | let _name: String 394 | let _value_type: String 395 | let _value_parser: String 396 | let _traits: Array[String] = [] // TODO: remove and use unions only 397 | 398 | new create(g: ASTGen, n: String, t: String, p: String) => 399 | (_gen, _name, _value_type, _value_parser) = (g, n, t, p) 400 | _gen.defs.push(this) 401 | 402 | fun ref in_union(n: String, n2: String = "") => 403 | _traits.push(n) 404 | if n2.size() > 0 then _traits.push(n2) end 405 | _gen._add_to_union(n, this) 406 | if n2.size() > 0 then _gen._add_to_union(n2, this) end 407 | 408 | fun name(): String => _name 409 | 410 | fun code_gen(g: CodeGen) => 411 | g.line("class val " + _name + " is ") 412 | if _traits.size() == 0 then 413 | g.add("AST") 414 | else 415 | g.add("(AST") 416 | for t in _traits.values() do g.add(" & " + t) end 417 | g.add(")") 418 | end 419 | g.push_indent() 420 | 421 | // Declare common fields. 422 | g.line("let _attachments: (Attachments | None)") 423 | 424 | // Declare the value field. 425 | g.line("let _value: " + _value_type) 426 | 427 | // Declare a constructor that initializes the value and pos from parameters. 428 | g.line("new val create(value': " + _value_type + ", ") 429 | g.add("attachments': (Attachments | None) = None) =>") 430 | g.push_indent() 431 | g.line("_value = value'") 432 | g.line("_attachments = attachments'") 433 | g.pop_indent() 434 | g.line() 435 | 436 | g.block( 437 | """ 438 | new from_iter( 439 | iter: Iterator[(AST | None)], 440 | pos': SourcePosAny = SourcePosNone, 441 | errs: Array[(String, SourcePosAny)] = [])? 442 | =>""") 443 | g.push_indent() 444 | g.line("_attachments = Attachments.attach_val[SourcePosAny](pos')") 445 | g.line("_value =") 446 | g.push_indent() 447 | g.line("try") 448 | g.push_indent() 449 | g.line(_value_parser + "(pos')?") 450 | g.pop_indent() 451 | g.line("else") 452 | g.push_indent() 453 | g.line("errs.push((\"" + _name + " failed to parse value\", pos')); true") 454 | g.line("error") 455 | g.pop_indent() 456 | g.line("end") 457 | g.pop_indent() 458 | g.line() 459 | g.line("if") 460 | g.push_indent() 461 | g.line("try") 462 | g.push_indent() 463 | g.line("let extra' = iter.next()?") 464 | g.line("errs.push((\"" + _name + " got unexpected extra field: \" + extra'.string(), try (extra' as AST).pos() else SourcePosNone end)); true") 465 | g.pop_indent() 466 | g.line("else false") 467 | g.line("end") 468 | g.pop_indent() 469 | g.line("then error end") 470 | g.pop_indent() 471 | g.line() 472 | 473 | // Declare common helpers. 474 | g.line("fun val apply_specialised[C](c: C, fn: {[A: AST val](C, A)} val) => fn[" + _name + "](consume c, this)") 475 | g.line("fun val get_child_dynamic(child': String, index': USize = 0): (AST | None)? => error") 476 | g.line("fun val with_replaced_child(child': AST, replace': (AST | None)): AST => this") 477 | 478 | // Declare common getters and setters. 479 | g.line("fun val pos(): SourcePosAny") 480 | g.add(" => try find_attached_val[SourcePosAny]()? else SourcePosNone end") 481 | g.line("fun val with_pos(pos': SourcePosAny): " + _name) 482 | g.add(" => attach_val[SourcePosAny](pos')") 483 | g.line() 484 | 485 | g.line("fun val size(): USize => 0") 486 | g.line("fun val apply(idx: USize): (AST | None)? => error") 487 | 488 | g.line("fun val attach_val[A: Any val](a: A): " + _name) 489 | g.add(" => create(_value, (try _attachments as Attachments else Attachments end).attach_val[A](a))") 490 | 491 | g.line("fun val attach_tag[A: Any tag](a: A): " + _name) 492 | g.add(" => create(_value, (try _attachments as Attachments else Attachments end).attach_tag[A](a))") 493 | 494 | g.line("fun val find_attached_val[A: Any val](): A? => (_attachments as Attachments).find_val[A]()?") 495 | 496 | g.line("fun val find_attached_tag[A: Any tag](): A? => (_attachments as Attachments).find_tag[A]()?") 497 | 498 | // Declare a getter method for the value field. 499 | g.line("fun val value(): " + _value_type + " => _value") 500 | 501 | // Declare a setter methods for the value field. 502 | g.line("fun val with_value(value': " + _value_type + "): " + _name + " => create(value', _attachments)") 503 | 504 | // Declare a string method to print itself. 505 | g.line("fun string(): String iso^ =>") 506 | g.push_indent() 507 | g.line("recover") 508 | g.push_indent() 509 | g.line("String.>append(\"" + _name + "(\")") 510 | g.add(".>append(_value.string()).>push(')')") 511 | g.pop_indent() 512 | g.line("end") 513 | g.pop_indent() 514 | 515 | g.pop_indent() 516 | g.line() 517 | 518 | class ASTGenDefLexeme is ASTGenDef 519 | let _gen: ASTGen 520 | let _name: String 521 | let _traits: Array[String] = [] // TODO: remove and use unions only 522 | 523 | new create(g: ASTGen, n: String) => 524 | (_gen, _name) = (g, n) 525 | _gen.defs.push(this) 526 | 527 | fun ref in_union(n: String, n2: String = "") => 528 | _traits.push(n) 529 | if n2.size() > 0 then _traits.push(n2) end 530 | _gen._add_to_union(n, this) 531 | if n2.size() > 0 then _gen._add_to_union(n2, this) end 532 | 533 | fun name(): String => _name 534 | 535 | fun code_gen(g: CodeGen) => 536 | g.line("class val " + _name + " is ") 537 | if _traits.size() == 0 then 538 | g.add("AST") 539 | else 540 | g.add("(AST") 541 | for t in _traits.values() do g.add(" & " + t) end 542 | g.add(")") 543 | end 544 | g.push_indent() 545 | 546 | // Declare a constructor that does nothing. 547 | g.line("new val create() => None") 548 | 549 | // Declare a constructor that accepts an iterator but always errors. 550 | g.block( 551 | """ 552 | new from_iter( 553 | iter: Iterator[(AST | None)], 554 | pos': SourcePosAny = SourcePosNone, 555 | errs: Array[(String, SourcePosAny)] = [])? 556 | =>""") 557 | g.push_indent() 558 | g.line("errs.push((\"" + _name + " is a lexeme-only type append should never be built\", pos')); error") 559 | g.pop_indent() 560 | g.line() 561 | 562 | // Declare common helpers. 563 | g.line("fun val apply_specialised[C](c: C, fn: {[A: AST val](C, A)} val) => fn[" + _name + "](consume c, this)") 564 | g.line("fun val get_child_dynamic(child': String, index': USize = 0): (AST | None)? => error") 565 | g.line("fun val with_replaced_child(child': AST, replace': (AST | None)): AST => this") 566 | 567 | // Declare common getters and setters. 568 | g.line("fun val pos(): SourcePosAny => SourcePosNone") 569 | g.line("fun val with_pos(pos': SourcePosAny): " + _name + " => create()") 570 | g.line() 571 | g.line("fun val size(): USize => 0") 572 | g.line("fun val apply(idx: USize): (AST | None)? => error") 573 | g.line() 574 | g.line("fun val attach_val[A: Any val](a: A): AST => this") 575 | g.line("fun val attach_tag[A: Any tag](a: A): AST => this") 576 | g.line("fun val find_attached_val[A: Any val](): A? => error") 577 | g.line("fun val find_attached_tag[A: Any tag](): A? => error") 578 | g.line() 579 | 580 | // Declare a string method to print itself. 581 | g.line("fun string(): String iso^ =>") 582 | g.push_indent() 583 | g.line("recover String.>append(\"" + _name + "\") end") 584 | g.pop_indent() 585 | 586 | g.pop_indent() 587 | g.line() 588 | 589 | class ASTGenDefActor is ASTGenDef 590 | let _gen: ASTGen 591 | let _name: String 592 | let vars: Array[(String, String, String)] = [] 593 | let lists: Array[(String, String, String)] = [] 594 | 595 | new create(g: ASTGen, n: String) => 596 | (_gen, _name) = (g, n) 597 | _gen.defs.push(this) 598 | 599 | fun name(): String => _name 600 | 601 | fun ref has_list(n: String, e: String, t: String) => lists.push((n, e, t)) 602 | 603 | fun ref has_var(n: String, t: String, d: String = "") => vars.push((n, t, d)) 604 | 605 | fun code_gen(g: CodeGen) => 606 | g.line("actor " + _name) 607 | g.push_indent() 608 | 609 | // Declare fields. 610 | for (list_name, _, elem_type) in lists.values() do 611 | g.line("let _" + list_name + ": Array[" + elem_type + "] = []") 612 | end 613 | for (var_name, var_type, _) in vars.values() do 614 | g.line("var _" + var_name + ": " + var_type) 615 | end 616 | 617 | // Declare constructor. 618 | g.line("new create(") 619 | for (idx, (var_name, var_type, var_default)) in vars.pairs() do 620 | if idx > 0 then g.add(", ") end 621 | g.add(var_name + "': " + var_type) 622 | if var_default != "" then g.add(" = " + var_default) end 623 | end 624 | g.add(") => ") 625 | for (idx, (var_name, _, _)) in vars.pairs() do 626 | if idx > 0 then g.add("; ") end 627 | g.add("_" + var_name + " = " + var_name + "'") 628 | else 629 | g.add("None") 630 | end 631 | 632 | // Declare field access methods. 633 | for (list_name, elem_name, elem_type) in lists.values() do 634 | g.line("be add_" + elem_name + "(e: " + elem_type + ") =>") 635 | g.add(" _" + list_name + ".push(e)") 636 | 637 | g.line("be access_" + list_name) 638 | g.add("(fn: {(Array[" + elem_type + "])} val) =>") 639 | g.add(" fn(_" + list_name + ")") 640 | end 641 | for (var_name, var_type, _) in vars.values() do 642 | g.line("be access_" + var_name) 643 | g.add("(fn: {(" + var_type + "): " + var_type + "} val) =>") 644 | g.add(" _" + var_name + "= fn(_" + var_name + ")") 645 | end 646 | 647 | g.pop_indent() 648 | g.line() 649 | -------------------------------------------------------------------------------- /ponycc/ast/gen/code_gen.pony: -------------------------------------------------------------------------------- 1 | 2 | use "collections" 3 | use "format" 4 | 5 | class CodeGen 6 | let _lines: List[String] = List[String] 7 | let _indents: List[String] = List[String] 8 | 9 | new iso create() => 10 | _indents.push("") 11 | 12 | fun string(): String => 13 | let out = recover trn String end 14 | for l in _lines.values() do out.append(l + "\n") end 15 | out 16 | 17 | fun ref add(s: String = "") => 18 | _lines.push(try _lines.pop()? else "" end + s) 19 | 20 | fun ref line(s: String = "") => 21 | _lines.push(current_indent() + s) 22 | 23 | fun ref block(s: String = "") => 24 | for s' in s.split_by("\n").values() do 25 | _lines.push(current_indent() + s') 26 | end 27 | 28 | fun ref push_indent(s: String = " ") => 29 | _indents.push(current_indent() + s) 30 | 31 | fun ref pop_indent() => 32 | try _indents.pop()? end 33 | 34 | fun ref current_indent(): String => 35 | try _indents.tail()?()? else "" end 36 | 37 | fun tag string_literal(s: String box): String => 38 | let out = recover trn String end 39 | out.push('"') 40 | for b' in s.values() do 41 | match b' 42 | | '"' => out.push('\\'); out.push('"') 43 | | '\\' => out.push('\\'); out.push('\\') 44 | | let b: U8 if b < 0x10 => out.append("\\x0" + Format.int[U8](b, FormatHexBare)) 45 | | let b: U8 if b < 0x20 => out.append("\\x" + Format.int[U8](b, FormatHexBare)) 46 | | let b: U8 if b < 0x7F => out.push(b) 47 | else let b = b'; out.append("\\x" + Format.int[U8](b, FormatHexBare)) 48 | end 49 | end 50 | out.push('"') 51 | consume out 52 | 53 | fun tag bytes_literal(a: Array[U8] box): String => 54 | if a.size() == 0 then return "recover val Array[U8] end" end 55 | let out = recover trn String end 56 | out.append("[as U8: ") 57 | 58 | let iter = a.values() 59 | for b in iter do 60 | out.append("0x" + Format.int[U8](b, FormatHexBare, PrefixDefault, 2)) 61 | if iter.has_next() then out.append(", ") end 62 | end 63 | 64 | out.push(']') 65 | consume out 66 | -------------------------------------------------------------------------------- /ponycc/ast/gen/main.pony: -------------------------------------------------------------------------------- 1 | 2 | actor Main 3 | new create(env: Env) => 4 | match try env.args(1)? else "" end 5 | | "ast" => let g = ASTGen; ASTDefs(g); env.out.print(g.string()) 6 | | "parser" => let g = ParserGen; ParserDefs(g); env.out.print(g.string()) 7 | | "" => abort(env, "missing expected 'ast' or 'parser' argument") 8 | else abort(env, "imvalid argument - expected 'ast' or 'parser'") 9 | end 10 | 11 | fun ref abort(env: Env, msg: String) => 12 | env.err.print(msg) 13 | @pony_exitcode[None](I32(1)) 14 | -------------------------------------------------------------------------------- /ponycc/ast/gen/parser_gen.pony: -------------------------------------------------------------------------------- 1 | 2 | class ParserGen 3 | embed defs: Array[ParserGenDef] = [] 4 | let debug: Bool = true 5 | 6 | new ref create() => None 7 | 8 | fun ref def(s: String): ParserGenDef => ParserGenDef(this, s) 9 | 10 | fun string(): String => 11 | let g: CodeGen = CodeGen 12 | 13 | g.line("use peg = \"peg\"") 14 | g.line("use \"../../ast\"") 15 | if debug then g.line("use \"debug\"") end 16 | g.line() 17 | g.line("class _Parser") 18 | g.push_indent() 19 | 20 | // TODO: Clean this up somehow. 21 | g.block( 22 | """ 23 | let tokens: Iterator[_Token] 24 | let errs: Seq[(String, SourcePosAny)] 25 | var token: _Token 26 | var last_helpful_token: _Token 27 | let errors: Array[String] = [] 28 | var failed: Bool = false 29 | var last_matched: String = "" 30 | 31 | new create(tokens': Iterator[_Token], errs': Seq[(String, SourcePosAny)]) => 32 | (tokens, errs) = (tokens', errs') 33 | token = 34 | try tokens.next()? 35 | else (Tk[EOF], SourcePosNone) 36 | end 37 | last_helpful_token = token 38 | 39 | fun ref parse(): (TkTree | None) => 40 | let expected = "class, actor, primitive or trait" // TODO: get this from where? 41 | match _parse_root(expected)._1 42 | | _RuleNotFound => _syntax_error(expected, None, ""); None 43 | | let tree: TkTree => if failed then None else tree end 44 | else None 45 | end 46 | 47 | fun _current_tk(): TkAny => 48 | token._1 49 | 50 | fun _current_pos(): SourcePosAny => 51 | token._2 52 | 53 | fun ref _consume_token(): _Token => 54 | let new_token = 55 | try tokens.next()? 56 | else (Tk[EOF], token._2) 57 | end 58 | 59 | match new_token._1 60 | | Tk[NewLine] | Tk[EOF] | Tk[None] => None 61 | else last_helpful_token = new_token 62 | end 63 | 64 | token = (new_token._1, last_helpful_token._2) 65 | 66 | fun ref _ditch_restart(state: _RuleState): _RuleResult => 67 | // Debug("Rule " + state.fn_name + ": Attempting recovery") // TODO: conditional compile 68 | 69 | while true do 70 | let tk = _current_tk() 71 | 72 | for restart_tk in state.restart.values() do 73 | if tk is restart_tk then 74 | // Debug(" recovered with " + tk.string()) // TODO: conditional compile 75 | return _RuleRestart 76 | end 77 | end 78 | 79 | // Debug(" ignoring " + tk.string()) // TODO: conditional compile 80 | 81 | _consume_token() 82 | end 83 | 84 | _RuleRestart // unreachable 85 | 86 | fun ref _handle_error(state: _RuleState): _RuleResult => 87 | state.tree = None 88 | failed = true 89 | 90 | if state.restart.size() == 0 then 91 | // Debug("Rule " + state.fn_name + ": Propagate failure") // TODO: conditional compile 92 | return _RuleParseError 93 | end 94 | 95 | _ditch_restart(state) 96 | 97 | fun _handle_found(state: _RuleState, tree: (TkTree | None), build: _Build): _RuleResult => 98 | if not state.matched then 99 | // Debug("Rule " + state.fn_name + ": Matched") // TODO: conditional compile 100 | 101 | state.matched = true 102 | end 103 | 104 | try state.add_tree(tree as TkTree, build) end 105 | 106 | state.default_tk = None 107 | None 108 | 109 | fun ref _handle_not_found(state: _RuleState, desc: String, terminating: Bool): _RuleResult => 110 | if state.default_tk isnt None then 111 | // Optional token / sub rule not found 112 | if state.default_tk isnt Tk[EOF] then // Default node is specified 113 | let tk = try state.default_tk as TkAny else Tk[EOF] end 114 | state.add_deferrable_ast((tk, _current_pos())) 115 | end 116 | 117 | state.default_tk = None 118 | return None 119 | end 120 | 121 | // Required token / sub rule not found 122 | 123 | if not state.matched then 124 | // Debug("Rule " + state.fn_name + ": Not matched") // TODO: conditional compile 125 | 126 | state.tree = None 127 | return _RuleNotFound 128 | end 129 | 130 | // Rule partially matched, error 131 | // Debug("Rule " + state.fn_name + ": Error") // TODO: conditional compile 132 | 133 | _syntax_error(desc, state.tree, if terminating then desc else "" end) 134 | failed = true 135 | state.tree = None 136 | 137 | if state.restart.size() == 0 then 138 | return _RuleParseError 139 | else 140 | return _ditch_restart(state) 141 | end 142 | 143 | fun ref _complete(state: _RuleState): _RuleResult => 144 | state.process_deferred_tree() 145 | 146 | // Debug("Rule " + state.fn_name + ": Complete") // TODO: conditional compile 147 | 148 | if state.restart.size() == 0 then return state.tree end 149 | 150 | // We have a restart point, check next token is legal 151 | let tk = _current_tk() 152 | 153 | // Debug("Rule " + state.fn_name + ": Check restart set for next token " + tk.string()) // TODO: conditional compile 154 | 155 | for restart_tk in state.restart.values() do 156 | if tk is restart_tk then 157 | // Debug("Rule " + state.fn_name + ": Restart check successful") // TODO: conditional compile 158 | return state.tree 159 | end 160 | end 161 | 162 | // Next token is not in restart set, error 163 | // Debug("Rule " + state.fn_name + ": Restart check error") // TODO: conditional compile 164 | 165 | _error("Parse error: Unexpected token " + _current_tk().desc() + " after " + state.desc, last_helpful_token._2) 166 | 167 | failed = true 168 | _ditch_restart(state) 169 | 170 | fun ref _error(str: String, pos: SourcePosAny) => 171 | errs.push((str, pos)) 172 | 173 | fun ref _error_continue(str: String, pos: SourcePosAny) => 174 | errs.push((str, pos)) // TODO: actually continue 175 | 176 | fun ref _syntax_error(expected: String, tree: (TkTree | None), terminating: String) => 177 | if last_matched.size() == 0 then 178 | _error("Parse error: No code found", last_helpful_token._2) 179 | else 180 | if terminating.size() == 0 then 181 | _error("Parse error: Expected " + expected + " after " + last_matched, last_helpful_token._2) 182 | else 183 | _error("Parse error: Unterminated " + terminating, 184 | try (tree as TkTree).pos else last_helpful_token._2 end) 185 | _error_continue("Expected terminating " + expected + " before here", last_helpful_token._2) 186 | end 187 | end 188 | """) 189 | 190 | // Declare each rule def. 191 | for d in defs.values() do 192 | d.code_gen(g) 193 | end 194 | 195 | g.pop_indent() 196 | 197 | g.string() 198 | -------------------------------------------------------------------------------- /ponycc/ast/gen/parser_gen_def.pony: -------------------------------------------------------------------------------- 1 | 2 | use "collections" 3 | 4 | class ParserGenDef 5 | // TODO: docs for all DSL methods 6 | let _gen: ParserGen 7 | let _name: String 8 | let _pieces: Array[{(CodeGen)}] = [] 9 | 10 | var _print_inline: Bool = false // TODO 11 | var _annotate: Bool = false // TODO 12 | var _builder: String = "_BuildDefault" 13 | 14 | var _restart: (None | Array[String]) = None 15 | 16 | new create(g: ParserGen, s: String) => 17 | (_gen, _name) = (g, s) 18 | _gen.defs.push(this) 19 | 20 | fun code_gen(g: CodeGen) => 21 | g.line("fun ref _parse_" + _name + "(rule_desc: String): (_RuleResult, _Build) =>") 22 | g.push_indent() 23 | g.line("let state = _RuleState(\"" + _name + "\", rule_desc)") 24 | g.line("var res: _RuleResult = None") 25 | g.line("var found: Bool = false") 26 | for piece in _pieces.values() do piece(g) end 27 | g.line() 28 | g.line("(_complete(state), " + _builder + ")") 29 | g.pop_indent() 30 | g.line() 31 | 32 | fun ref print_inline() => _print_inline = true // TODO 33 | fun ref annotate() => _annotate = true // TODO 34 | 35 | fun ref builder(builder': String) => _builder = builder' 36 | 37 | fun ref tree(s: String) => 38 | _pieces.push({(g) => 39 | g.line("state.add_deferrable_ast((" + s + ", _current_pos()))") 40 | }) 41 | 42 | fun ref restart(a: Array[String]) => _restart = a // TODO 43 | 44 | fun ref _rule_set(desc: String, array: Array[String], default_tk: String = "None") => 45 | _pieces.push({(g) => 46 | if _gen.debug then 47 | g.line() 48 | let opt = if default_tk is "None" then "required" else "optional" end 49 | // g.line("Debug(\"Rule " + _name + ": Looking for " + opt + " rule(s) '" + desc + "'\")") 50 | end 51 | g.line() 52 | g.line("state.default_tk = " + default_tk) 53 | g.line("found = false") 54 | g.line("res =") 55 | g.push_indent() 56 | g.line("while true do") 57 | g.push_indent() 58 | for rule in array.values() do 59 | g.line("match _parse_" + rule + "(\"" + desc + "\")") 60 | g.line("| (_RuleParseError, _) => break _handle_error(state)") 61 | g.line("| (let tree: (TkTree | None), let build: _Build) =>") 62 | g.push_indent() 63 | g.line("found = true") 64 | g.line("last_matched = \"" + desc + "\"") 65 | g.line("break _handle_found(state, tree, build)") 66 | g.pop_indent() 67 | g.line("end") 68 | g.line() 69 | end 70 | g.line("found = false") 71 | g.line("break _handle_not_found(state, \"" + desc + "\", false)") 72 | g.pop_indent() 73 | g.line("end") 74 | g.pop_indent() 75 | g.line("if res isnt None then return (res, " + _builder + ") end") 76 | }) 77 | 78 | fun ref rule(desc: String, array: Array[String]) => 79 | _rule_set(desc, array) 80 | 81 | fun ref opt_rule(desc: String, array: Array[String], 82 | default: String = "Tk[None]") 83 | => 84 | _rule_set(desc, array, default) 85 | 86 | fun ref opt_no_dflt_rule(desc: String, array: Array[String]) => 87 | _rule_set(desc, array, "Tk[EOF]") 88 | 89 | fun ref _token_set( 90 | desc': String, 91 | array: Array[String], 92 | default_tk: String = "None", 93 | make_ast: Bool = true, 94 | terminating: Bool = false) 95 | => 96 | let desc = 97 | if desc' == "None" then 98 | try array(0)? else "Tk[EOF]" end + ".desc()" 99 | else 100 | "\"" + desc' + "\"" 101 | end 102 | 103 | _pieces.push({(g) => 104 | if _gen.debug then 105 | g.line() 106 | let opt = if default_tk is "None" then "required" else "optional" end 107 | // g.line("Debug(\"Rule " + _name + ": Looking for " + opt + " rule(s) '\" + " + desc + " + \"'. Found \" + _current_tk().string())") 108 | end 109 | g.line() 110 | g.line("state.default_tk = " + default_tk) 111 | g.line("found = false") 112 | 113 | // Skip past any occurrences of NewLine, unless that's one of our set. 114 | if not array.contains("Tk[NewLine]", {(a: String, b: String): Bool => a == b }) then 115 | g.line("while _current_tk() is Tk[NewLine] do _consume_token() end") 116 | end 117 | 118 | g.line("res =") 119 | g.push_indent() 120 | g.line("match _current_tk()") 121 | for (idx, tk) in array.pairs() do 122 | g.add(" | ") 123 | g.add(tk) 124 | end 125 | g.add(" =>") 126 | g.push_indent() 127 | // if _gen.debug then g.line("Debug(\"Compatible\")") end 128 | g.line("found = true") 129 | g.line("last_matched = " + desc) 130 | if make_ast then 131 | g.line("_handle_found(state, TkTree(_consume_token()), _BuildDefault)") 132 | else 133 | g.line("_handle_found(state, (_consume_token(); None), _BuildDefault)") 134 | end 135 | g.pop_indent() 136 | g.line("else") 137 | g.push_indent() 138 | // if _gen.debug then g.line("Debug(\"Not compatible\")") end 139 | g.line("found = false") 140 | g.line("_handle_not_found(state, " + desc + ", " + terminating.string() + ")") 141 | g.pop_indent() 142 | g.line("end") 143 | g.pop_indent() 144 | g.line("if res isnt None then return (res, " + _builder + ") end") 145 | }) 146 | 147 | fun ref token(desc: String, array: Array[String]) => 148 | _token_set(desc, array) 149 | 150 | fun ref opt_token(desc: String, array: Array[String]) => 151 | _token_set(desc, array, "Tk[None]") 152 | 153 | fun ref opt_no_dflt_token(desc: String, array: Array[String]) => 154 | _token_set(desc, array, "Tk[EOF]") 155 | 156 | fun ref not_token(desc: String, array: Array[String]) => 157 | _token_set("None", array, "Tk[EOF]" where make_ast = false) 158 | _pieces.push({(g) => 159 | g.line("if found then return (_RuleNotFound, " + _builder + ") end") 160 | }) 161 | 162 | fun ref if_token_then_rule(tk: String, desc: String, array: Array[String]) => 163 | _token_set("None", [tk], "Tk[EOF]" where make_ast = false) 164 | _pieces.push({(g) => g.line("if found then"); g.push_indent() }) 165 | _rule_set(desc, array) 166 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 167 | 168 | fun ref if_token_then_rule_else_none(tk: String, desc: String, 169 | array: Array[String]) 170 | => 171 | _token_set("None", [tk], "Tk[None]" where make_ast = false) 172 | _pieces.push({(g) => g.line("if found then"); g.push_indent() }) 173 | _rule_set(desc, array) 174 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 175 | 176 | fun ref while_token_do_rule(tk: String, desc: String, array: Array[String]) => 177 | _pieces.push({(g) => g.line("while true do"); g.push_indent() }) 178 | _token_set("None", [tk], "Tk[EOF]" where make_ast = false) 179 | _pieces.push({(g) => g.line("if not found then break end") }) 180 | _rule_set(desc, array) 181 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 182 | 183 | fun ref if_token_then_token(tk: String, desc: String, array: Array[String]) => 184 | _token_set("None", [tk], "Tk[EOF]" where make_ast = false) 185 | _pieces.push({(g) => g.line("if found then"); g.push_indent() }) 186 | _token_set(desc, array) 187 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 188 | 189 | fun ref while_token_do_token(tk: String, desc: String, array: Array[String]) => 190 | _pieces.push({(g) => g.line("while true do"); g.push_indent() }) 191 | _token_set("None", [tk], "Tk[EOF]" where make_ast = false) 192 | _pieces.push({(g) => g.line("if not found then break end") }) 193 | _token_set(desc, array) 194 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 195 | 196 | fun ref seq(desc: String, array: Array[String]) => 197 | _pieces.push({(g) => g.line("while true do"); g.push_indent() }) 198 | _rule_set(desc, array, "Tk[EOF]") 199 | _pieces.push({(g) => g.line("if not found then break end") }) 200 | _pieces.push({(g) => g.pop_indent(); g.line("end") }) 201 | 202 | fun ref skip(desc: String, array: Array[String]) => 203 | _token_set(desc, array where make_ast = false) 204 | 205 | fun ref terminate(desc: String, array: Array[String]) => 206 | _token_set(desc, array where make_ast = false, terminating = true) 207 | 208 | fun ref map_tk(array: Array[(String, String)]) => 209 | _pieces.push({(g) => 210 | g.line("match state.tree | let tree: TkTree =>") 211 | g.push_indent() 212 | g.line("match tree.tk") 213 | for (src, dest) in array.values() do 214 | g.line("| " + src + " => tree.tk = " + dest) 215 | end 216 | g.line("end") 217 | g.pop_indent() 218 | g.line("end") 219 | }) 220 | 221 | fun ref reorder_children(array: Array[USize]) => 222 | _pieces.push({(g) => 223 | g.line("match state.tree | let tree: TkTree =>") 224 | g.push_indent() 225 | for i in Range(0, array.size()) do 226 | g.line("let child_" + i.string()) 227 | g.add(" = try tree.children.shift()? else TkTree(token) end") 228 | end 229 | for i in array.values() do 230 | g.line("tree.children.push(child_" + i.string() + ")") 231 | end 232 | g.pop_indent() 233 | g.line("end") 234 | }) 235 | 236 | fun ref rotate_left_children(count: USize) => 237 | _pieces.push({(g) => 238 | g.line("match state.tree | let tree: TkTree =>") 239 | g.push_indent() 240 | for i in Range(0, count) do 241 | g.line("try tree.children.push(tree.children.shift()?) end") 242 | end 243 | g.pop_indent() 244 | g.line("end") 245 | }) 246 | -------------------------------------------------------------------------------- /ponycc/ast/program.pony: -------------------------------------------------------------------------------- 1 | actor _ResultCollector[A: Any #share] 2 | var _finish_fn: {(Array[A] val)} val 3 | var _results: Array[A] trn = [] 4 | var _expecting: USize = 0 5 | 6 | new create(finish_fn': {(Array[A] val)} val) => 7 | _finish_fn = finish_fn' 8 | 9 | be push_expectation() => _expecting = _expecting + 1 10 | be pop_expectation() => _pop_expectation() 11 | fun ref _pop_expectation() => 12 | if 1 >= (_expecting = _expecting - 1) then complete() end 13 | 14 | be apply(a: A) => 15 | _results.push(a) 16 | _pop_expectation() 17 | 18 | fun ref complete() => 19 | _finish_fn(_results = []) 20 | _finish_fn = {(results) => None } 21 | 22 | class val Program 23 | let _inner: _Program = _Program 24 | 25 | new val create() => None 26 | 27 | fun add_package(e: Package) => 28 | _inner.add_package(e) 29 | 30 | fun access_packages(fn: {(Array[Package])} val) => 31 | _inner.access_packages(fn) 32 | 33 | fun get_all_type_decls(fn: {(Array[TypeDecl] val)} val) => 34 | let collector = _ResultCollector[TypeDecl](fn) 35 | access_packages({(packages)(collector) => 36 | for package in packages.values() do 37 | collector.push_expectation() 38 | package.access_type_decls({(type_decls)(collector) => 39 | for type_decl in type_decls.values() do 40 | collector.push_expectation() 41 | type_decl.access_type_decl({(ast) => 42 | collector(ast) 43 | ast 44 | }) 45 | end 46 | collector.pop_expectation() 47 | }) 48 | end 49 | }) 50 | 51 | fun _get_type_decl_by_idx(idx: USize, fn: {((TypeDecl | None))} val) => 52 | access_packages({(packages)(fn, idx) => 53 | try 54 | packages(0)?.access_type_decls({(type_decls)(fn, idx) => 55 | try 56 | type_decls(idx)?.access_type_decl({(ast) => 57 | fn(ast) 58 | ast 59 | }) 60 | else fn(None) 61 | end 62 | }) 63 | else fn(None) 64 | end 65 | }) 66 | 67 | fun get_child_dynamic_path(path: String, fn: {((AST | None))} val) => 68 | let ast_fn: {((AST | None))} val = {(ast')(fn) => 69 | var ast: (AST | None) = ast' 70 | let crumbs = path.split_by(".").values() 71 | 72 | try 73 | crumbs.next()? // skip the first crumb - it was used already 74 | for crumb in crumbs do 75 | let pieces = crumb.split_by("-", 2) 76 | 77 | ast = 78 | (ast as AST).get_child_dynamic( 79 | pieces(0)?, 80 | try pieces(1)?.usize()? else 0 end)? 81 | end 82 | else ast = None 83 | end 84 | 85 | fn(ast) 86 | } 87 | 88 | let crumbs = path.split_by(".").values() 89 | try 90 | let top_pieces = crumbs.next()?.split_by("-", 2) 91 | match top_pieces(0)? 92 | | "type_decls" => 93 | let idx = top_pieces(1)?.usize()? 94 | _get_type_decl_by_idx(idx, ast_fn) 95 | else fn(None) 96 | end 97 | else fn(None) 98 | end 99 | -------------------------------------------------------------------------------- /ponycc/ast/source.pony: -------------------------------------------------------------------------------- 1 | 2 | use "collections" 3 | 4 | type Sources is ReadSeq[Source] val 5 | 6 | interface val SourceAny is Comparable[SourceAny box] 7 | fun content(): String 8 | fun path(): String 9 | 10 | fun eq(that: SourceAny box): Bool => path() == that.path() 11 | fun lt(that: SourceAny box): Bool => path() < that.path() 12 | 13 | primitive SourceNone is SourceAny 14 | fun content(): String => "" 15 | fun path(): String => "" 16 | 17 | class val Source is SourceAny 18 | let _content: String 19 | let _path: String 20 | 21 | new val create(content': String = "", path': String = "") => 22 | (_content, _path) = (content', path') 23 | 24 | fun content(): String => _content 25 | fun path(): String => _path 26 | 27 | interface val SourcePosAny is Comparable[SourcePosAny box] 28 | fun source(): SourceAny 29 | fun offset(): USize 30 | fun length(): USize 31 | 32 | fun eq(that: SourcePosAny box): Bool => 33 | (source() == that.source()) and 34 | (offset() == that.offset()) and 35 | (length() == that.length()) 36 | 37 | fun lt(that: SourcePosAny box): Bool => 38 | (source() < that.source()) or ( 39 | (source() == that.source()) and ( 40 | (offset() < that.offset()) or ( 41 | (offset() == that.offset()) and 42 | (length() < that.length()) 43 | ) 44 | ) 45 | ) 46 | 47 | fun string(): String => 48 | source().content().trim(offset(), offset() + length()) 49 | 50 | fun entire_line(): SourcePosAny => 51 | let str = source().content() 52 | 53 | let i = try (str.rfind("\n", offset().isize())? + 1).usize() else 0 end 54 | let j = try str.find("\n", offset().isize())?.usize() else str.size() end 55 | 56 | SourcePos(source(), i, j - i) 57 | 58 | fun show_in_line(): (String, String) => 59 | let l = entire_line() 60 | 61 | let arrow = recover String(l.length()) end 62 | for i in Range(0, offset() - l.offset().min(offset())) do arrow.push(' ') end 63 | arrow.push('^') 64 | if length() >= 1 then 65 | for i in Range(0, length() - 1) do arrow.push('~') end 66 | end 67 | 68 | (l.string(), consume arrow) 69 | 70 | primitive SourcePosNone is SourcePosAny 71 | fun source(): SourceAny => SourceNone 72 | fun offset(): USize => 0 73 | fun length(): USize => 0 74 | 75 | class val SourcePos is SourcePosAny 76 | let _source: SourceAny 77 | let _offset: USize 78 | let _length: USize 79 | 80 | new val create(source': SourceAny, offset': USize = 0, length': USize = 0) => 81 | (_source, _offset, _length) = (source', offset', length') 82 | 83 | fun source(): SourceAny => _source 84 | fun offset(): USize => _offset 85 | fun length(): USize => _length 86 | -------------------------------------------------------------------------------- /ponycc/ast/uuid.pony: -------------------------------------------------------------------------------- 1 | 2 | class val Uuid 3 | new val create() => None 4 | -------------------------------------------------------------------------------- /ponycc/compiler.pony: -------------------------------------------------------------------------------- 1 | 2 | use "pass" 3 | use "frame" 4 | 5 | type _ErrsFn is {(PassAny, Array[PassError] val)} val 6 | 7 | interface val _CompilerPrev[I: Any val = None, O: Any val = None] 8 | fun val _compile(input: I, on_errors': _ErrsFn, on_complete': {(O)} val) 9 | 10 | primitive BuildCompiler 11 | fun apply[I: Any val, O: Any val](p: Pass[I, O]): Compiler[I, O, I] => 12 | Compiler[I, O, I](p) 13 | 14 | class val Compiler[I: Any val, O: Any val, M: Any val = I] 15 | let _prev: (_CompilerPrev[I, M] | None) 16 | let _pass: Pass[M, O] 17 | let _on_errors: _ErrsFn 18 | let _on_complete: {(O)} val 19 | 20 | new val create(pass': Pass[M, O]) => 21 | _prev = None 22 | _pass = pass' 23 | _on_errors = {(_, _) => None } 24 | _on_complete = {(_) => None } 25 | 26 | new val _create( 27 | prev': (_CompilerPrev[I, M] | None), 28 | pass': Pass[M, O], 29 | on_errors': _ErrsFn = {(_, _) => None }, 30 | on_complete': {(O)} val = {(_) => None }) 31 | => 32 | _prev = prev' 33 | _pass = pass' 34 | _on_errors = on_errors' 35 | _on_complete = on_complete' 36 | 37 | fun val on_errors(fn: _ErrsFn): Compiler[I, O, M] => 38 | """ 39 | Set the errors handling function for the compiler. 40 | This function will be invoked after finishing a pass if there were errors. 41 | 42 | The first argument to the function will be the pass that was finished. 43 | The second argument will be the list of all errors encountered in that pass. 44 | """ 45 | Compiler[I, O, M]._create(_prev, _pass, fn, _on_complete) 46 | 47 | fun val on_complete(fn: {(O)} val): Compiler[I, O, M] => 48 | """ 49 | Set the completion handling function for this compiler. 50 | This function will be invoked after finishing all passes with no errors. 51 | 52 | The only argument to the function will be the finished AST. 53 | """ 54 | Compiler[I, O, M]._create(_prev, _pass, _on_errors, fn) 55 | 56 | fun val next[O2: Any val](pass': Pass[O, O2]): Compiler[I, O2, O] => 57 | """ 58 | Chain another pass onto this compiler, where the input of the new pass 59 | must be the same type as the output of the currently held pass. 60 | 61 | This should be called before setting on_errors or on_complete, as all 62 | previously set handler functions will be discarded here. 63 | """ 64 | Compiler[I, O2, O]._create(this, pass') 65 | 66 | fun val apply(input: I) => 67 | _compile(input, _on_errors, _on_complete) 68 | 69 | fun val _compile(input: I, on_errors': _ErrsFn, on_complete': {(O)} val) => 70 | let pass' = _pass 71 | match _prev | let prev': _CompilerPrev[I, M] => 72 | prev'._compile(input, on_errors', {(middle)(on_errors', on_complete') => 73 | pass'.apply(middle, {(output: O, errs: Array[PassError] val) => // TODO: use apply sugar, use lambda inference 74 | if errs.size() > 0 then 75 | on_errors'(pass', errs) 76 | else 77 | on_complete'(output) 78 | end 79 | }) 80 | }) 81 | else 82 | match input | let middle: M => 83 | pass'.apply(middle, {(output: O, errs: Array[PassError] val) => // TODO: use apply sugar, use lambda inference 84 | if errs.size() > 0 then 85 | on_errors'(pass', errs) 86 | else 87 | on_complete'(output) 88 | end 89 | }) 90 | else 91 | None // TODO: should call on_errors with an error message here... 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /ponycc/frame/_frame_continuation.pony: -------------------------------------------------------------------------------- 1 | class _FrameContinuation[V: FrameVisitor[V]] 2 | embed indices: Array[USize] = [] 3 | let continue_fn: {(Frame[V], Any val)} val 4 | var value: Any val = None 5 | 6 | new create(fn: {(Frame[V], Any val)} val) => continue_fn = fn 7 | 8 | fun ref _push_index(idx: USize) => indices.push(idx) 9 | 10 | fun clone(): _FrameContinuation[V] iso^ => 11 | let copy = recover _FrameContinuation[V](continue_fn) end 12 | for i in indices.values() do copy._push_index(i) end 13 | copy.value = value 14 | consume copy 15 | -------------------------------------------------------------------------------- /ponycc/frame/_frame_reactor.pony: -------------------------------------------------------------------------------- 1 | use "../ast" 2 | use "../pass" 3 | use poly = "../polyfill" 4 | use "collections" 5 | 6 | actor _FrameReactor[V: FrameVisitor[V]] 7 | let _program: Program 8 | var _complete_fn: {(Program, Array[PassError] val)} val 9 | var _expectations: USize = 0 10 | embed _errs: Array[PassError] = _errs.create() 11 | embed _continuations: 12 | Array[(Program, Package, PackageTypeDecl, _FrameContinuation[V] iso)] = 13 | _continuations.create() 14 | embed _ready_to_continue: 15 | MapIs[_FrameContinuation[V] tag, Any val] = 16 | _ready_to_continue.create() 17 | 18 | new create(program: Program, fn: {(Program, Array[PassError] val)} val) => 19 | (_program, _complete_fn) = (program, fn) 20 | visit_program(_program) 21 | 22 | be err(a: AST, s: String) => 23 | _errs.push(PassError(a.pos(), s)) 24 | 25 | be _push_expectation() => _expectations = _expectations + 1 26 | be _pop_expectation() => 27 | _expectations = _expectations - 1 28 | if _expectations == 0 then _complete() end 29 | 30 | be continue_with(c: _FrameContinuation[V] tag, value: Any val) => 31 | var found = false 32 | try while true do 33 | (let prog, let pkg, let td, let continuation) = _continuations.shift()? 34 | if continuation is c then 35 | found = true 36 | continuation.value = value 37 | visit_type_decl(prog, pkg, td, consume continuation) 38 | _pop_expectation() 39 | else 40 | _continuations.push((prog, pkg, td, consume continuation)) 41 | end 42 | end end 43 | if not found then _ready_to_continue(c) = value end 44 | 45 | fun ref _complete() => 46 | if (_continuations.size() > 0) and (_errs.size() == 0) then 47 | _errs.push(PassError(SourcePosNone, "compiler deadlock")) 48 | // TODO: show info about pending continuations here. 49 | end 50 | 51 | poly.Sort[PassError](_errs) 52 | let copy = recover Array[PassError] end 53 | for e in _errs.values() do copy.push(e) end 54 | (_complete_fn = {(_, _) => _ })(_program, consume copy) 55 | 56 | be _track_result( 57 | program: Program, 58 | package: Package, 59 | type_decl: PackageTypeDecl, 60 | result: (_FrameContinuation[V] iso | None) = None) 61 | => 62 | match consume result | let continuation: _FrameContinuation[V] iso => 63 | if _ready_to_continue.contains(continuation) then 64 | try 65 | (_, let value) = _ready_to_continue.remove(continuation)? 66 | continuation.value = value 67 | visit_type_decl(program, package, type_decl, consume continuation) 68 | _pop_expectation() 69 | end 70 | else 71 | _continuations.push((program, package, type_decl, consume continuation)) 72 | end 73 | end 74 | 75 | fun tag visit_program(program: Program) => 76 | _push_expectation() 77 | program.access_packages({(packages)(reactor = this) => 78 | for package in packages.values() do 79 | reactor.visit_package(program, package) 80 | end 81 | reactor._pop_expectation() 82 | }) 83 | 84 | fun tag visit_package(program: Program, package: Package) => 85 | _push_expectation() 86 | package.access_type_decls({(type_decls)(reactor = this) => 87 | for type_decl in type_decls.values() do 88 | reactor.visit_type_decl(program, package, type_decl) 89 | end 90 | reactor._pop_expectation() 91 | }) 92 | 93 | fun tag visit_type_decl( 94 | program: Program, 95 | package: Package, 96 | type_decl: PackageTypeDecl, 97 | continue_from: (_FrameContinuation[V] val | None) = None) 98 | => 99 | _push_expectation() 100 | type_decl.access_type_decl({(ast')(reactor = this) => 101 | // TODO: remove the need for this clone 102 | let continue_from' = 103 | try (continue_from as _FrameContinuation[V] val).clone() end 104 | 105 | var ast = ast' 106 | reactor._track_result(program, package, type_decl, 107 | recover 108 | let top = _FrameTop[V](reactor, program, package, type_decl, ast) 109 | let frame = Frame[V]._create_under(top, ast) 110 | let continuation = frame._visit(consume continue_from') 111 | ast = top.type_decl() 112 | match continuation | let c: _FrameContinuation[V] => 113 | reactor._push_expectation() 114 | end 115 | continuation 116 | end 117 | ) 118 | reactor._pop_expectation() 119 | ast 120 | }) 121 | 122 | be view_each_ffi_decl(fn: {(UseFFIDecl)} val) => 123 | let reactor: _FrameReactor[V] = this 124 | reactor._push_expectation() 125 | _program.access_packages({(packages)(reactor, fn) => 126 | for package in packages.values() do 127 | reactor._push_expectation() 128 | package.access_ffi_decls({(ffi_decls)(reactor, fn) => 129 | for ffi_decl in ffi_decls.values() do 130 | fn(ffi_decl) 131 | end 132 | reactor._pop_expectation() 133 | }) 134 | end 135 | reactor._pop_expectation() 136 | }) 137 | -------------------------------------------------------------------------------- /ponycc/frame/frame.pony: -------------------------------------------------------------------------------- 1 | use "../ast" 2 | use "../pass" 3 | use "../unreachable" 4 | use "promises" 5 | 6 | interface val FrameVisitor[V: FrameVisitor[V]] 7 | new val create() 8 | fun visit[A: AST val](frame: Frame[V], a: A) 9 | 10 | primitive FrameVisitorNone is FrameVisitor[FrameVisitorNone] 11 | fun visit[A: AST val](frame: Frame[FrameVisitorNone], a: A) => None 12 | 13 | class val FrameRunner[V: FrameVisitor[V]] 14 | """ 15 | Visit all of the AST nodes in the given program, using the given FrameVisitor. 16 | 17 | The order of visitation is depth-first, with children being visited before 18 | their parent node is visited. Children have no knowledge of their parent, 19 | other than through the separate Frame object that is part of the visitation. 20 | """ 21 | let _reactor: _FrameReactor[V] 22 | 23 | new val create(program: Program, fn: {(Program, Array[PassError] val)} val) => 24 | _reactor = _FrameReactor[V](program, fn) 25 | 26 | fun err(a: AST, s: String) => _reactor.err(a, s) 27 | 28 | fun view_each_ffi_decl(fn: {(UseFFIDecl)} val) => 29 | _reactor.view_each_ffi_decl(fn) 30 | 31 | actor _Seeker[A: Any #share] 32 | var _open: USize = 0 33 | var _found: Bool = false 34 | let _promise: Promise[A] 35 | new create(p: Promise[A]) => _promise = p 36 | be apply(a: A) => if not (_found = true) then _promise(a) end 37 | be open() => _open = _open + 1 38 | be close() => _open = _open - 1 39 | if (not _found) and (_open == 0) then _promise.reject() end 40 | 41 | class _FrameTop[V: FrameVisitor[V]] 42 | let _reactor: _FrameReactor[V] 43 | let _program: Program 44 | let _package: Package 45 | let _type_decl: PackageTypeDecl 46 | var _ast: TypeDecl 47 | 48 | new create( 49 | reactor': _FrameReactor[V], 50 | program': Program, 51 | package': Package, 52 | type_decl': PackageTypeDecl, 53 | ast': TypeDecl) 54 | => 55 | (_reactor, _program, _package, _type_decl, _ast) = 56 | (reactor', program', package', type_decl', ast') 57 | 58 | fun _r(): _FrameReactor[V] => _reactor 59 | 60 | fun err(a: AST, s: String) => _reactor.err(a, s) 61 | 62 | fun parent(n: USize): AST => _ast // ignore n - we can't go any higher 63 | fun ref replace(a: AST) => try _ast = a as TypeDecl end 64 | fun program(): Program => _program 65 | fun package(): Package => _package 66 | fun type_decl(): TypeDecl => _ast 67 | fun method(): (Method | None) => None 68 | fun method_body(): (Sequence | None) => None 69 | fun constraint(): (Type | None) => None 70 | fun iftype_constraint(): (Type | None) => None 71 | 72 | fun find_type_decl(package_id': (Id | None), id: Id): Promise[TypeDecl] => 73 | let reactor = _reactor 74 | reactor._push_expectation() 75 | let promise = Promise[TypeDecl].>next[None]( 76 | {(_) => reactor._pop_expectation()}, 77 | {() => reactor._pop_expectation()}) 78 | let seeker = _Seeker[TypeDecl](promise) 79 | match package_id' 80 | | let package_id: Id => 81 | seeker.open() 82 | _type_decl.access_use_packages({(use_packages)(id, seeker) => 83 | for use_package in use_packages.values() do 84 | if 85 | try (use_package.prefix() as Id).value() == package_id.value() 86 | else false 87 | end 88 | then 89 | try 90 | let package' = use_package.find_attached_tag[Package]()? 91 | seeker.open() 92 | package'.access_type_decls({(type_decls)(id, seeker) => 93 | for type_decl in type_decls.values() do 94 | seeker.open() 95 | type_decl.access_type_decl({(type_decl)(id, seeker) => 96 | if id.value() == type_decl.name().value() then 97 | seeker(type_decl) 98 | end 99 | seeker.close() 100 | type_decl 101 | }) 102 | end 103 | seeker.close() 104 | }) 105 | end 106 | end 107 | end 108 | seeker.close() 109 | }) 110 | else 111 | seeker.open() 112 | _package.access_type_decls({(type_decls)(id, seeker) => 113 | for type_decl in type_decls.values() do 114 | seeker.open() 115 | type_decl.access_type_decl({(type_decl)(id, seeker) => 116 | if id.value() == type_decl.name().value() then 117 | seeker(type_decl) 118 | end 119 | seeker.close() 120 | type_decl 121 | }) 122 | end 123 | seeker.close() 124 | }) 125 | end 126 | promise 127 | 128 | class Frame[V: FrameVisitor[V]] 129 | let _upper: (Frame[V] | _FrameTop[V]) 130 | var _ast: AST 131 | var _maybe_continuation: (_FrameContinuation[V] | None) = None 132 | 133 | new _create_under(upper': (Frame[V] | _FrameTop[V]), a: AST) => 134 | _upper = upper' 135 | _ast = a 136 | 137 | fun ref _visit(continue_from: (_FrameContinuation[V] | None) = None) 138 | : (_FrameContinuation[V] | None) 139 | => 140 | """ 141 | Visit the given AST node in a new frame, after visiting its children. 142 | """ 143 | let continue_from_idx = 144 | match continue_from 145 | | let c: _FrameContinuation[V] => 146 | try 147 | let idx = c.indices.pop()? 148 | if idx == -1 then 149 | c.continue_fn(this, c.value) 150 | return _maybe_continuation 151 | end 152 | idx 153 | else 0 154 | end 155 | else 0 156 | end 157 | 158 | for (idx, child) in _ast.pairs() do 159 | if idx >= continue_from_idx then 160 | match child | let child_ast: AST => 161 | match Frame[V]._create_under(this, child_ast)._visit(continue_from) 162 | | let continuation: _FrameContinuation[V] => 163 | continuation.indices.push(idx) 164 | return continuation 165 | end 166 | end 167 | end 168 | end 169 | 170 | _ast.apply_specialised[Frame[V]](this, 171 | {[A: AST val](frame, a: A) => V.visit[A](frame, a) }) 172 | 173 | _maybe_continuation 174 | 175 | fun _r(): _FrameReactor[V] => _upper._r() 176 | 177 | fun err(a: AST, s: String) => 178 | """ 179 | Emit an error, indicating a problem with the given AST node, including the 180 | given message as a human-friendly explanation of the problem. 181 | """ 182 | _upper.err(a, s) 183 | 184 | fun parent(n: USize = 1): AST => 185 | """ 186 | Get the n-th AST node above this one. 187 | 188 | If n is zero, the AST node associated with the current Frame is returned. 189 | If n is too high, the uppermost AST node in this Frame stack is returned. 190 | """ 191 | if n == 0 then _ast else _upper.parent(n - 1) end 192 | 193 | fun ref replace(replace': AST) => 194 | """ 195 | Replace the current AST node for this Frame with the given replacement AST. 196 | 197 | If the given AST is not the correct type for replacing this slot in the 198 | parent AST, or if the Frame's current AST is not actually a child of the 199 | parent AST, the replacement operation will silently fail, with no effect. 200 | """ 201 | if _ast isnt replace' then 202 | if parent() is _ast then // TODO: less hacky dealing with FrameTop. 203 | _upper.replace(replace') 204 | else 205 | _upper.replace(parent().with_replaced_child(_ast, replace')) 206 | end 207 | _ast = replace' 208 | end 209 | 210 | fun ref await[A: Any val]( 211 | promise: Promise[A], 212 | fn: {(Frame[V], (A | None))} val) 213 | => 214 | """ 215 | Cause AST traversal to pause when the current visit function is done with 216 | the current Frame, and set it up so that when the given promise is fulfilled 217 | the given fn will be called with the result (or None if rejected), alongside 218 | a new mutable Frame that is ready to continue traversing the AST. 219 | """ 220 | let continuation = _FrameContinuation[V]({(frame, value) => 221 | try fn(frame, value as (A | None)) else Unreachable end 222 | }) 223 | 224 | // TODO: consider what to do when there may be more than one continuation. 225 | continuation.indices.push(-1) 226 | _maybe_continuation = continuation 227 | 228 | let c: _FrameContinuation[V] tag = continuation 229 | 230 | promise.next[None]( 231 | {(value)(r = _r()) => r.continue_with(c, value) }, 232 | {()(r = _r()) => r.continue_with(c, None) }) 233 | 234 | fun find_type_decl(package_id': (Id | None), id: Id): Promise[TypeDecl] => 235 | """ 236 | Search for a TypeDecl that has been imported into the current Module scope, 237 | with an optional package id prefix for id-scoped package imports. 238 | """ 239 | _upper.find_type_decl(package_id', id) 240 | 241 | fun program(): Program => 242 | """ 243 | Get the nearest Program above this AST node. 244 | """ 245 | _upper.program() 246 | 247 | fun package(): Package => 248 | """ 249 | Get the nearest Package above this AST node. 250 | """ 251 | _upper.package() 252 | 253 | fun type_decl(): TypeDecl => 254 | """ 255 | Get the nearest TypeDecl above this AST node. 256 | """ 257 | _upper.type_decl() 258 | 259 | fun method(): (Method | None) => 260 | """ 261 | Get the nearest Method above this AST node. 262 | """ 263 | try _ast as Method else _upper.method() end 264 | 265 | fun method_body(): (Sequence | None) => 266 | """ 267 | Get the nearest Method body Sequence above this AST node. 268 | Stop searching through the hierarchy when a Method is reached. 269 | """ 270 | match parent() | let m: Method => 271 | if _ast is m.body() then m.body() else None end 272 | else 273 | _upper.method_body() 274 | end 275 | 276 | fun constraint(): (Type | None) => 277 | """ 278 | Get the nearest TypeParam constraint Type above this AST node. 279 | Stop searching through the hierarchy when a TypeParam is reached. 280 | Also stop searching at TypeArgs - type arguments of a constraining Type 281 | are not counted as being a part of the constraining Type themselves. 282 | """ 283 | match parent() 284 | | let _: TypeArgs => None 285 | | let t: TypeParam => 286 | if _ast is t.constraint() then t.constraint() else None end 287 | else 288 | _upper.constraint() 289 | end 290 | 291 | fun iftype_constraint(): (Type | None) => 292 | """ 293 | Get the nearest IfType constraint (super) Type above this AST node. 294 | Stop searching through the hierarchy when an IfType is reached. 295 | Also stop searching at TypeArgs - type arguments of a constraining Type 296 | are not counted as being a part of the constraining Type themselves. 297 | """ 298 | match parent() 299 | | let _: TypeArgs => None 300 | | let i: IfType => 301 | if _ast is i.super() then i.super() else None end 302 | else 303 | _upper.iftype_constraint() 304 | end 305 | -------------------------------------------------------------------------------- /ponycc/pass/names/names.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | use "../../frame" 5 | 6 | primitive Names is (Pass[Program, Program] & FrameVisitor[Names]) 7 | """ 8 | The purpose of the Names pass is... 9 | 10 | This pass only adds attachments to the AST, and is idempotent. 11 | """ 12 | fun name(): String => "names" 13 | 14 | fun apply(ast: Program, fn: {(Program, Array[PassError] val)} val) => 15 | FrameRunner[Names](ast, fn) 16 | 17 | fun visit[A: AST val](frame: Frame[Names], ast: A) => 18 | iftype A <: NominalType then 19 | let promise = frame.find_type_decl(ast.package(), ast.name()) 20 | frame.await[TypeDecl](promise, {(frame, type_decl) => 21 | frame.err(ast, "This is a reference.") 22 | try 23 | frame.err((type_decl as TypeDecl).name(), 24 | "This is a referenced type.") 25 | end 26 | }) 27 | end 28 | -------------------------------------------------------------------------------- /ponycc/pass/parse/_build.pony: -------------------------------------------------------------------------------- 1 | 2 | interface val _Build 3 | new val create() 4 | fun apply(state: _RuleState, tree: TkTree) 5 | 6 | primitive _BuildDefault 7 | fun apply(state: _RuleState, tree: TkTree) => 8 | try (state.tree as TkTree).children.push(tree) end 9 | 10 | primitive _BuildInfix 11 | fun apply(state: _RuleState, tree: TkTree) => 12 | try tree.children.unshift(state.tree as TkTree) end 13 | state.tree = tree 14 | 15 | primitive _BuildCustomNominalType 16 | fun apply(state: _RuleState, tree: TkTree) => 17 | _BuildDefault(state, TkTree((tree.tk, tree.pos))) 18 | 19 | for (i, child) in tree.children.pairs() do 20 | if (tree.children.size() == 3) and (i == 0) 21 | then _BuildDefault(state, TkTree((Tk[None], child.pos))) 22 | end 23 | _BuildDefault(state, child) 24 | end 25 | -------------------------------------------------------------------------------- /ponycc/pass/parse/_lexer.pony: -------------------------------------------------------------------------------- 1 | use peg = "peg" 2 | use "../../ast" 3 | 4 | type _L is peg.L 5 | type _R is peg.R 6 | 7 | class _Lexer 8 | let parser: peg.Parser val = recover 9 | let digit = _R('0', '9') 10 | let digits = digit * digit.many() 11 | let bin = _R('0', '1') 12 | let hex = digit / _R('a', 'f') / _R('A', 'F') 13 | let alpha = _R('a', 'z') / _R('A', 'Z') 14 | 15 | let eol_comment = _L("//") * (not _L("\n") * peg.Unicode).many() 16 | let eol_item = eol_comment 17 | 18 | let inline_comment = peg.Forward 19 | inline_comment() = 20 | _L("/*") 21 | * ((not _L("/*") * not _L("*/") * peg.Unicode) / inline_comment).many() 22 | * _L("*/") 23 | 24 | let string1_char = 25 | _L("\\\"") / _L("\\\\") / _L("\\f") / _L("\\n") / _L("\\r") / _L("\\t") 26 | / _L("\\b") / (_L("\\x") * hex * hex) / (_L("\\u") * hex * hex * hex * hex) 27 | / (not _L("\"") * not _L("\\") * peg.Unicode) 28 | let string1_literal = 29 | (_L("\"") * string1_char.many() * _L("\"")) 30 | 31 | let string3_literal = 32 | _L("\"\"\"") * (not _L("\"\"\"") * peg.Unicode).many() * _L("\"\"\"") 33 | 34 | let string_literal = (string3_literal / string1_literal).term(Tk[LitString]) 35 | 36 | let char_char = 37 | _L("\\'") / _L("\\\\") / _L("\\f") / _L("\\n") / _L("\\r") / _L("\\t") 38 | / _L("\\b") / (_L("\\x") * hex * hex) / (_L("\\u") * hex * hex * hex * hex) 39 | / (not _L("'") * not _L("\\") * peg.Unicode) 40 | let char_literal = 41 | (_L("'") * char_char * _L("'")).term(Tk[LitCharacter]) 42 | 43 | let float_literal = 44 | (digits * ( 45 | (_L(".") * digits * (_L("e") / _L("E")) * digits) 46 | / ((_L(".") / _L("e") / _L("E")) * digits) 47 | )).term(Tk[LitFloat]) 48 | 49 | let int_literal = 50 | ( 51 | ((_L("0x") / _L("0X")) * hex * hex.many()) 52 | / ((_L("0b") / _L("0B")) * bin * bin.many()) 53 | / digits 54 | ).term(Tk[LitInteger]) 55 | 56 | let ident_start = alpha / _L("_") 57 | let ident_char = ident_start / digit / _L("'") 58 | let ident = (ident_start * ident_char.many()).term(Tk[Id]) 59 | 60 | let lexicon = _Lexicon 61 | let keyword = lexicon.keywords * (not ident_char) 62 | let symbol = lexicon.symbols 63 | let newline_symbol = lexicon.newline_symbols 64 | 65 | let normal_item = 66 | inline_comment 67 | / string_literal 68 | / char_literal 69 | / float_literal 70 | / int_literal 71 | / keyword 72 | / symbol 73 | / ident 74 | 75 | let s = (_L(" ") / _L("\t") / _L("\r")).many() 76 | 77 | let line = 78 | s * newline_symbol.opt() 79 | * (s * not eol_item * normal_item).many() 80 | * (s * eol_item.opt()) 81 | * s 82 | 83 | (line * _L("\n").term(Tk[NewLine])).many() * line 84 | end 85 | 86 | fun apply(source: SourceAny): (Array[_Token] iso^ | USize) => 87 | let source' = peg.Source.from_string(source.content(), source.path()) 88 | (let i, let p) = parser.parse(source') 89 | 90 | match p | let ast: peg.AST => 91 | if i == source.content().size() 92 | then _flatten(ast) 93 | else i 94 | end 95 | else i 96 | end 97 | 98 | fun tag _flatten( 99 | ast: peg.AST, 100 | array': Array[_Token] iso = recover Array[_Token] end) 101 | : Array[_Token] iso^ 102 | => 103 | var array: Array[_Token] iso = consume array' 104 | for child in ast.children.values() do 105 | array = 106 | match child 107 | | let ast': peg.AST => _flatten(ast', consume array) 108 | | let token: peg.Token => 109 | match token.label() | let tk: TkAny => 110 | let source = Source(token.source.content, token.source.path) 111 | let source_pos = SourcePos(source, token.offset, token.length) 112 | (consume array).>push((tk, source_pos)) 113 | else 114 | consume array 115 | end 116 | else 117 | consume array 118 | end 119 | end 120 | consume array 121 | -------------------------------------------------------------------------------- /ponycc/pass/parse/_lexicon.pony: -------------------------------------------------------------------------------- 1 | 2 | use peg = "peg" 3 | use "../../ast" 4 | 5 | class val _Lexicon 6 | // Note that for keywords where one keyword starts with another, 7 | // the longer one must appear first in this list. 8 | // For example "ifdef" must appear before "if". 9 | let keywords: peg.Parser val = recover 10 | peg.L("_").term(Tk[DontCare]) 11 | 12 | / peg.L("use").term(Tk[Use]) 13 | / peg.L("type").term(Tk[TypeAlias]) 14 | / peg.L("interface").term(Tk[Interface]) 15 | / peg.L("trait").term(Tk[Trait]) 16 | / peg.L("primitive").term(Tk[Primitive]) 17 | / peg.L("struct").term(Tk[Struct]) 18 | / peg.L("class").term(Tk[Class]) 19 | / peg.L("actor").term(Tk[Actor]) 20 | / peg.L("object").term(Tk[Object]) 21 | 22 | / peg.L("iso").term(Tk[Iso]) 23 | / peg.L("trn").term(Tk[Trn]) 24 | / peg.L("ref").term(Tk[Ref]) 25 | / peg.L("val").term(Tk[Val]) 26 | / peg.L("box").term(Tk[Box]) 27 | / peg.L("tag").term(Tk[Tag]) 28 | 29 | / peg.L("as").term(Tk[As]) 30 | / peg.L("isnt").term(Tk[Isnt]) 31 | / peg.L("is").term(Tk[Is]) 32 | 33 | / peg.L("var").term(Tk[Var]) 34 | / peg.L("let").term(Tk[Let]) 35 | / peg.L("embed").term(Tk[Embed]) 36 | / peg.L("new").term(Tk[MethodNew]) 37 | / peg.L("fun").term(Tk[MethodFun]) 38 | / peg.L("be").term(Tk[MethodBe]) 39 | 40 | / peg.L("this").term(Tk[This]) 41 | / peg.L("return").term(Tk[Return]) 42 | / peg.L("break").term(Tk[Break]) 43 | / peg.L("continue").term(Tk[Continue]) 44 | / peg.L("consume").term(Tk[Consume]) 45 | / peg.L("recover").term(Tk[Recover]) 46 | 47 | / peg.L("ifdef").term(Tk[IfDef]) 48 | / peg.L("iftype").term(Tk[IfType]) 49 | / peg.L("if").term(Tk[If]) 50 | / peg.L("then").term(Tk[Then]) 51 | / peg.L("elseif").term(Tk[ElseIf]) 52 | / peg.L("else").term(Tk[Else]) 53 | / peg.L("end").term(Tk[End]) 54 | / peg.L("for").term(Tk[For]) 55 | / peg.L("in").term(Tk[In]) 56 | / peg.L("while").term(Tk[While]) 57 | / peg.L("do").term(Tk[Do]) 58 | / peg.L("repeat").term(Tk[Repeat]) 59 | / peg.L("until").term(Tk[Until]) 60 | / peg.L("match").term(Tk[Match]) 61 | / peg.L("where").term(Tk[Where]) 62 | / peg.L("try").term(Tk[Try]) 63 | / peg.L("with").term(Tk[With]) 64 | / peg.L("error").term(Tk[Error]) 65 | / peg.L("compile_error").term(Tk[CompileError]) 66 | / peg.L("compile_intrinsic").term(Tk[CompileIntrinsic]) 67 | 68 | / peg.L("not").term(Tk[Not]) 69 | / peg.L("and").term(Tk[And]) 70 | / peg.L("or").term(Tk[Or]) 71 | / peg.L("xor").term(Tk[XOr]) 72 | 73 | / peg.L("digestof").term(Tk[DigestOf]) 74 | / peg.L("addressof").term(Tk[AddressOf]) 75 | / peg.L("__loc").term(Tk[LitLocation]) 76 | 77 | / peg.L("true").term(Tk[LitTrue]) 78 | / peg.L("false").term(Tk[LitFalse]) 79 | 80 | / peg.L("#read").term(Tk[CapRead]) 81 | / peg.L("#send").term(Tk[CapSend]) 82 | / peg.L("#share").term(Tk[CapShare]) 83 | / peg.L("#alias").term(Tk[CapAlias]) 84 | / peg.L("#any").term(Tk[CapAny]) 85 | end 86 | 87 | // Note that for symbols where one symbol starts with another, 88 | // the longer one must appear first in this list. 89 | // For example ">=" must appear before ">". 90 | let symbols: peg.Parser val = recover 91 | peg.L("...").term(Tk[Ellipsis]) 92 | 93 | / peg.L("==~").term(Tk[EqUnsafe]) 94 | / peg.L("!=~").term(Tk[NEUnsafe]) 95 | / peg.L("<=~").term(Tk[LEUnsafe]) 96 | / peg.L(">=~").term(Tk[GEUnsafe]) 97 | / peg.L("<<~").term(Tk[LShiftUnsafe]) 98 | / peg.L(">>~").term(Tk[RShiftUnsafe]) 99 | 100 | / peg.L("==").term(Tk[Eq]) 101 | / peg.L("!=").term(Tk[NE]) 102 | / peg.L("<=").term(Tk[LE]) 103 | / peg.L(">=").term(Tk[GE]) 104 | / peg.L("<<").term(Tk[LShift]) 105 | / peg.L(">>").term(Tk[RShift]) 106 | / peg.L("<:").term(Tk[SubType]) 107 | 108 | / peg.L(".>").term(Tk[Chain]) 109 | / peg.L("->").term(Tk[Arrow]) 110 | / peg.L("=>").term(Tk[DoubleArrow]) 111 | 112 | / peg.L("+~").term(Tk[AddUnsafe]) 113 | / peg.L("-~").term(Tk[SubUnsafe]) 114 | / peg.L("*~").term(Tk[MulUnsafe]) 115 | / peg.L("/~").term(Tk[DivUnsafe]) 116 | / peg.L("%~").term(Tk[ModUnsafe]) 117 | / peg.L("<~").term(Tk[LTUnsafe]) 118 | / peg.L(">~").term(Tk[GTUnsafe]) 119 | 120 | / peg.L("+").term(Tk[Add]) 121 | / peg.L("-").term(Tk[Sub]) 122 | / peg.L("*").term(Tk[Mul]) 123 | / peg.L("/").term(Tk[Div]) 124 | / peg.L("%").term(Tk[Mod]) 125 | / peg.L("<").term(Tk[LT]) 126 | / peg.L(">").term(Tk[GT]) 127 | 128 | / peg.L("@{").term(Tk[AtLBrace]) 129 | / peg.L("{").term(Tk[LBrace]) 130 | / peg.L("}").term(Tk[RBrace]) 131 | / peg.L("(").term(Tk[LParen]) 132 | / peg.L(")").term(Tk[RParen]) 133 | / peg.L("[").term(Tk[LSquare]) 134 | / peg.L("]").term(Tk[RSquare]) 135 | / peg.L(",").term(Tk[Comma]) 136 | 137 | / peg.L(".").term(Tk[Dot]) 138 | / peg.L("~").term(Tk[Tilde]) 139 | / peg.L(":").term(Tk[Colon]) 140 | / peg.L(";").term(Tk[Semicolon]) 141 | / peg.L("=").term(Tk[Assign]) 142 | 143 | / peg.L("@").term(Tk[At]) 144 | / peg.L("|").term(Tk[Pipe]) 145 | / peg.L("&").term(Tk[Ampersand]) 146 | / peg.L("^").term(Tk[Ephemeral]) 147 | / peg.L("!").term(Tk[Aliased]) 148 | 149 | / peg.L("?").term(Tk[Question]) 150 | / peg.L("#").term(Tk[Constant]) 151 | / peg.L("\\").term(Tk[Annotation]) 152 | end 153 | 154 | // Note that for symbols where one symbol starts with another, 155 | // the longer one must appear first in this list. 156 | // For example "-~" must appear before "-". 157 | let newline_symbols: peg.Parser val = recover 158 | peg.L("-~").term(Tk[SubUnsafeNew]) 159 | / peg.L("-").term(Tk[SubNew]) 160 | / peg.L("(").term(Tk[LParenNew]) 161 | / peg.L("[").term(Tk[LSquareNew]) 162 | end 163 | -------------------------------------------------------------------------------- /ponycc/pass/parse/_rule.pony: -------------------------------------------------------------------------------- 1 | 2 | use peg = "peg" 3 | 4 | class _RuleState 5 | let fn_name: String // Name of the current function, for tracing 6 | let desc: String // Rule description (set by parent) 7 | var tree: (TkTree | None) = None // Tree built for this rule 8 | let restart: Array[TkAny] = [] // Restart token set, NULL for none 9 | var default_tk: (TkAny | None) = None // ID of node to create when an optional token or rule is not found. 10 | // - TkEOF = do not create a default 11 | // - None = rule is not optional 12 | var matched: Bool = false // Has the rule matched yet 13 | var deferred: (_Token | None) = None // Deferred token, if any 14 | 15 | new create(n: String, d: String) => (fn_name, desc) = (n, d) 16 | 17 | fun ref add_tree(t: TkTree, build: _Build) => 18 | process_deferred_tree() 19 | 20 | if tree is None 21 | then tree = t 22 | else build(this, t) 23 | end 24 | 25 | fun ref add_deferrable_ast(defer: _Token) => 26 | if (not matched) 27 | and (tree is None) 28 | and (deferred is None) 29 | then 30 | deferred = defer 31 | return 32 | end 33 | 34 | add_tree(TkTree(defer), _BuildDefault) 35 | 36 | fun ref process_deferred_tree() => 37 | try 38 | tree = TkTree(deferred as _Token) 39 | deferred = None 40 | end 41 | 42 | primitive _RuleParseError 43 | primitive _RuleNotFound 44 | primitive _RuleRestart 45 | 46 | type _RuleResult is (_RuleParseError | _RuleNotFound | _RuleRestart | TkTree | None) 47 | -------------------------------------------------------------------------------- /ponycc/pass/parse/parse.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | 5 | primitive Parse is Pass[Source, Module] 6 | fun name(): String => "parse" 7 | 8 | fun apply(source: Source, fn: {(Module, Array[PassError] val)} val) => 9 | var module = Module 10 | let errs = recover val 11 | let err_tuples: Array[(String, SourcePosAny)] = [] 12 | module = try Parser(source, err_tuples)? else Module end 13 | 14 | let errs = Array[PassError](err_tuples.size()) 15 | for (message, pos) in err_tuples.values() do 16 | errs.push(PassError(pos, message)) 17 | end 18 | errs 19 | end 20 | 21 | if errs.size() > 0 then 22 | fn(module, errs) 23 | else 24 | ParseCleanup(module, fn) 25 | end 26 | 27 | class val Parser 28 | let _lexer: _Lexer = _Lexer 29 | 30 | new val create() => None 31 | 32 | fun apply[A: AST val = Module]( // TODO: remove cap when https://github.com/ponylang/ponyc/pull/2675 is merged 33 | source: Source, 34 | errs: Array[(String, SourcePosAny)] = []) 35 | : A ? 36 | => 37 | match _lexer(source) 38 | | let tokens: Array[(TkAny, SourcePosAny)] val => 39 | let parser = _Parser(tokens.values(), errs) 40 | match parser.parse() 41 | | let tree: TkTree => 42 | let ast = tree.to_ast(errs)? 43 | match ast 44 | | let a: A => return a 45 | else 46 | errs.push(("Expected to parse an AST of type " + ASTInfo.name[A]() 47 | + ", but got " + tree.tk.string(), tree.pos)) 48 | end 49 | else 50 | if errs.size() == 0 then 51 | errs.push(("Unspecified syntax error", parser.token._2)) 52 | end 53 | end 54 | | let err_idx: USize => 55 | errs.push(("Unknown syntax", SourcePos(source, err_idx, 1))) 56 | end 57 | 58 | error 59 | -------------------------------------------------------------------------------- /ponycc/pass/parse/parse_cleanup.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | use "../../frame" 5 | use "../../unreachable" 6 | 7 | primitive ParseCleanup is (Pass[Module, Module] & FrameVisitor[ParseCleanup]) 8 | """ 9 | The purpose of the ParseCleanup pass is to check and/or clean up any oddities 10 | in the AST related to the specific implementation of the parser or grammar. 11 | 12 | Checks or cleanup operations that would also need to also be done for AST 13 | nodes that had been created directly in Pony code do not belong here 14 | (they probably belong in the Sugar pass, or later). 15 | 16 | This pass changes the AST, and is not idempotent. 17 | """ 18 | fun name(): String => "parse-cleanup" 19 | 20 | fun apply(module: Module, fn: {(Module, Array[PassError] val)} val) => 21 | let program = Program 22 | let package = Package 23 | for t in module.type_decls().values() do 24 | package.add_type_decl(PackageTypeDecl(t)) 25 | end 26 | program.add_package(package) 27 | 28 | FrameRunner[ParseCleanup](program, {(program, errs)(module, fn) => 29 | program.get_all_type_decls({(type_decls) => 30 | fn(module.with_type_decls(type_decls), errs) 31 | }) 32 | }) 33 | 34 | fun visit[A: AST val](frame: Frame[ParseCleanup], ast: A) => 35 | iftype A <: Method then 36 | match ast.body() | let body: Sequence => 37 | try 38 | frame.err(ast.docs() as LitString, 39 | "A method with a body cannot have a docstring in the signature.") 40 | else 41 | // If the method body has more than one expression and the first 42 | // expression is a string literal, we treat it as a docstring; 43 | // we set it as the docs of the method and remove it from the body. 44 | if body.list().size() > 1 then 45 | try let docs = body.list()(0)? as LitString 46 | let ast': Method = ast // TODO: remove this when ponyc crash is fixed 47 | frame.replace(ast' 48 | .with_docs(docs) 49 | .with_body(body.with_list(body.list().delete(0)?))) 50 | end 51 | end 52 | end 53 | end 54 | 55 | elseif A <: BinaryOp then 56 | if 57 | match ast.left() 58 | | let _: A => false 59 | | let _: BinaryOp => true 60 | else false 61 | end 62 | or 63 | match ast.right() 64 | | let _: A => false 65 | | let _: BinaryOp => true 66 | else false 67 | end 68 | then 69 | frame.err(ast, 70 | "Operator precedence is not supported. Parentheses are required.") 71 | end 72 | 73 | elseif A <: TupleType then 74 | // If the tuple type contains just one element, we unwrap it. 75 | // It isn't a tuple type; just a type that was surrounded by parentheses. 76 | if ast.list().size() == 1 then 77 | try frame.replace(ast.list()(0)?) end 78 | end 79 | 80 | elseif A <: Semicolon then 81 | frame.err(ast, 82 | "Use semicolons only for separating expressions on the same line.") 83 | end 84 | -------------------------------------------------------------------------------- /ponycc/pass/parse/parse_program_files.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | use "collections" 5 | 6 | class val ParseProgramFiles is Pass[Sources, Program] 7 | """ 8 | TODO: Docs for this agreggated pass 9 | """ 10 | let _resolve_sources: {(String, String): (String, Sources)?} val 11 | 12 | fun name(): String => "parse-program-files" 13 | 14 | new val create(resolve_sources': {(String, String): (String, Sources)?} val) => 15 | _resolve_sources = resolve_sources' 16 | 17 | fun apply(sources: Sources, fn: {(Program, Array[PassError] val)} val) => 18 | _ParseProgramFilesEngine(_resolve_sources, fn).start(sources) 19 | 20 | actor _ParseProgramFilesEngine 21 | let _pending: SetIs[Source] = _pending.create() 22 | let _packages: Array[Package] = [] 23 | var _errs: Array[PassError] trn = [] 24 | let _complete_fn: {(Program, Array[PassError] val)} val 25 | let _resolve_sources: {(String, String): (String, Sources)?} val 26 | 27 | new create( 28 | resolve_sources': {(String, String): (String, Sources)?} val, 29 | complete_fn': {(Program, Array[PassError] val)} val) 30 | => 31 | (_resolve_sources, _complete_fn) = (resolve_sources', complete_fn') 32 | 33 | be start(sources: Sources, package: Package = Package) => 34 | for source in sources.values() do 35 | _pending.set(source) 36 | let this_tag: _ParseProgramFilesEngine = this 37 | Parse(source, this_tag~after_parse(source, package)) // TODO: fix ponyc to let plain `this` work here 38 | end 39 | 40 | be after_parse( 41 | source: Source, package: Package, 42 | module': Module, errs: Array[PassError] val) 43 | => 44 | var module = consume module' 45 | 46 | // Take note of having finished parsing this source. 47 | _pending.unset(source) 48 | 49 | // Take note of any errors. 50 | for err in errs.values() do _errs.push(err) end 51 | 52 | let use_packages = Array[UsePackage] 53 | 54 | // Call start for the source files of any referenced packages. 55 | for u' in module.use_decls().values() do 56 | match u' 57 | | let u: UseFFIDecl => package.add_ffi_decl(u) 58 | | let u: UsePackage => 59 | try 60 | (let package_path, let sources) = 61 | _resolve_sources(u.pos().source().path(), u.package().value())? 62 | 63 | // TODO: assign same Package to packages with the same absolute path. 64 | let new_package = Package 65 | use_packages.push(u.attach_tag[Package](new_package)) 66 | start(sources, new_package) 67 | else 68 | _errs.push(PassError(u.package().pos(), 69 | "Couldn't resolve this package directory.")) 70 | end 71 | end 72 | end 73 | 74 | for t in module.type_decls().values() do 75 | let type_decl = PackageTypeDecl(t) 76 | for u in use_packages.values() do type_decl.add_use_package(u) end 77 | package.add_type_decl(type_decl) 78 | end 79 | 80 | try package.add_doc(module.docs() as LitString) end 81 | 82 | _packages.push(package) 83 | 84 | _maybe_complete() 85 | 86 | be _maybe_complete() => 87 | """ 88 | If there are no more pending sources left, run the completion logic. 89 | This is in a separate behaviour so that causal message order ensures that 90 | this happens after any start calls in the same after_parse execution. 91 | """ 92 | if _pending.size() == 0 then _complete() end 93 | 94 | fun ref _complete() => 95 | let program = Program 96 | 97 | // Collect the modules into packages. 98 | for package in _packages.values() do 99 | program.add_package(package) 100 | end 101 | 102 | _complete_fn(program, _errs = []) 103 | -------------------------------------------------------------------------------- /ponycc/pass/parse/tk.pony: -------------------------------------------------------------------------------- 1 | 2 | use coll = "collections/persistent" 3 | use peg = "peg" 4 | use "../../ast" 5 | 6 | trait val TkAny is peg.Label 7 | fun text(): String => string() // Required for peg library, but otherwise unused. 8 | fun string(): String 9 | fun desc(): String 10 | fun _from_iter( 11 | iter: Iterator[(AST | None)], 12 | pos': SourcePosAny = SourcePosNone, 13 | errs: Array[(String, SourcePosAny)] = []) 14 | : (AST ref | None) ? 15 | 16 | primitive Tk[A: (AST | None)] is TkAny 17 | fun string(): String => ASTInfo.name[A]() 18 | fun desc(): String => ASTInfo.name[A]() 19 | fun _from_iter( 20 | iter: Iterator[(AST | None)], 21 | pos': SourcePosAny = SourcePosNone, 22 | errs: Array[(String, SourcePosAny)] = []) 23 | : (AST ref | None) ? 24 | => 25 | if false then error end // TODO: fix ponyc, then remove this 26 | iftype A <: AST 27 | then A.from_iter(iter, pos', errs) 28 | else None 29 | end 30 | 31 | type _Token is (TkAny, SourcePosAny) 32 | 33 | class TkTree 34 | var tk: TkAny 35 | var pos: SourcePosAny 36 | embed children: Array[TkTree] = [] 37 | // TODO: annotations 38 | 39 | new ref create(token: _Token) => (tk, pos) = token 40 | 41 | fun string(): String => _show() 42 | 43 | fun _show(buf': String iso = recover String end): String iso^ => 44 | var buf = consume buf' 45 | let nonterminal = children.size() > 0 46 | 47 | if nonterminal then buf.push('(') end 48 | buf.append(tk.string()) 49 | 50 | for child in children.values() do 51 | buf.push(' ') 52 | buf = child._show(consume buf) 53 | end 54 | 55 | if nonterminal then buf.push(')') end 56 | buf 57 | 58 | fun to_ast( 59 | errs: Array[(String, SourcePosAny)] = []) 60 | : (AST | None) ? 61 | => 62 | let ast_children = recover Array[(AST | None)] end 63 | for child in children.values() do 64 | ast_children.push(child.to_ast(errs)?) 65 | end 66 | 67 | // TODO: is there a less convoluted way to lift the AST to val? 68 | var errs' = recover Array[(String, SourcePosAny)] end 69 | try 70 | recover val 71 | let errs'' = Array[(String, SourcePosAny)] 72 | try 73 | tk._from_iter((consume ast_children).values(), pos, errs'')? 74 | else 75 | for err in errs''.values() do errs'.push(err) end 76 | error 77 | end 78 | end 79 | else 80 | for err in (consume errs').values() do errs.push(err) end 81 | error 82 | end 83 | -------------------------------------------------------------------------------- /ponycc/pass/pass.pony: -------------------------------------------------------------------------------- 1 | interface val PassAny 2 | fun name(): String 3 | 4 | interface val Pass[A: Any #share, B: Any #share] 5 | fun name(): String 6 | fun apply(a: A, fn: {(B, Array[PassError] val)} val) 7 | -------------------------------------------------------------------------------- /ponycc/pass/pass_error.pony: -------------------------------------------------------------------------------- 1 | use "../ast" 2 | 3 | class val PassError is Comparable[PassError box] 4 | let pos: SourcePosAny 5 | let message: String 6 | 7 | new val create(p: SourcePosAny, m: String) => (pos, message) = (p, m) 8 | 9 | fun eq(that: PassError box): Bool => 10 | (pos == that.pos) and 11 | (message == that.message) 12 | 13 | fun lt(that: PassError box): Bool => 14 | (pos < that.pos) or 15 | ((pos == that.pos) and (message < that.message)) 16 | -------------------------------------------------------------------------------- /ponycc/pass/sugar/sugar.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | use "../../frame" 5 | use "../../unreachable" 6 | 7 | use coll = "collections/persistent" 8 | 9 | primitive Sugar is (Pass[Program, Program] & FrameVisitor[Sugar]) 10 | """ 11 | The purpose of the Sugar pass is to fill in implicit details and expand 12 | abbreviated forms to their more verbose equivalents. 13 | 14 | Changes that require any kind of larger context (such as other packages, 15 | scoping, name resolution, type information) do not belong here. 16 | 17 | This pass changes the AST, but it is idempotent. 18 | """ 19 | fun name(): String => "sugar" 20 | 21 | fun apply(ast: Program, fn: {(Program, Array[PassError] val)} val) => 22 | FrameRunner[Sugar](ast, fn) 23 | 24 | fun _find_member(members: Members, name': String): (Method | Field | None) => 25 | for method in members.methods().values() do 26 | if method.name().value() == name' then return method end 27 | end 28 | for field in members.fields().values() do 29 | if field.name().value() == name' then return field end 30 | end 31 | 32 | fun _unary_op_method_id[A: UnaryOp val](): (Id | None) => 33 | iftype A <: Not then Id("op_not") 34 | elseif A <: Neg then Id("neg") 35 | elseif A <: NegUnsafe then Id("neg_unsafe") 36 | else None 37 | end 38 | 39 | fun _binary_op_method_id[A: BinaryOp val](): (Id | None) => 40 | iftype A <: Add then Id("add") 41 | elseif A <: AddUnsafe then Id("add_unsafe") 42 | elseif A <: Sub then Id("sub") 43 | elseif A <: SubUnsafe then Id("sub_unsafe") 44 | elseif A <: Mul then Id("mul") 45 | elseif A <: MulUnsafe then Id("mul_unsafe") 46 | elseif A <: Div then Id("div") 47 | elseif A <: DivUnsafe then Id("div_unsafe") 48 | elseif A <: Mod then Id("mod") 49 | elseif A <: ModUnsafe then Id("mod_unsafe") 50 | elseif A <: LShift then Id("shl") 51 | elseif A <: LShiftUnsafe then Id("shl_unsafe") 52 | elseif A <: RShift then Id("shr") 53 | elseif A <: RShiftUnsafe then Id("shr_unsafe") 54 | elseif A <: Eq then Id("eq") 55 | elseif A <: EqUnsafe then Id("eq_unsafe") 56 | elseif A <: NE then Id("ne") 57 | elseif A <: NEUnsafe then Id("ne_unsafe") 58 | elseif A <: LT then Id("lt") 59 | elseif A <: LTUnsafe then Id("lt_unsafe") 60 | elseif A <: LE then Id("le") 61 | elseif A <: LEUnsafe then Id("le_unsafe") 62 | elseif A <: GE then Id("ge") 63 | elseif A <: GEUnsafe then Id("ge_unsafe") 64 | elseif A <: GT then Id("gt") 65 | elseif A <: GTUnsafe then Id("gt_unsafe") 66 | elseif A <: And then Id("op_and") 67 | elseif A <: Or then Id("op_or") 68 | elseif A <: XOr then Id("op_xor") 69 | else None 70 | end 71 | 72 | fun visit[A: AST val](frame: Frame[Sugar], ast': A) => 73 | // TODO: from sugar.c - sugar_module 74 | // TODO: from sugar.c - sugar_typeparam 75 | 76 | iftype A <: TypeDecl then 77 | var ast: TypeDecl = ast' 78 | 79 | // Apply a default cap to the type declaration. 80 | if ast.cap() is None then 81 | iftype A <: (Interface | Trait) then ast = ast.with_cap(Ref) 82 | elseif A <: (Struct | Class) then ast = ast.with_cap(Ref) 83 | elseif A <: Primitive then ast = ast.with_cap(Val) 84 | elseif A <: Actor then ast = ast.with_cap(Tag) 85 | end 86 | end 87 | 88 | // Add a default constructor if there is no `create` member. 89 | iftype A <: (Primitive | Struct | Class | Actor) then 90 | if _find_member(ast.members(), "create") is None then 91 | let cap = 92 | iftype A <: (Struct | Class) then Iso 93 | elseif A <: Primitive then Val 94 | elseif A <: Actor then Tag 95 | else Unreachable(ast); Tag 96 | end 97 | 98 | let method = 99 | MethodNew(Id("create"), cap where body' = Sequence([LitTrue])) 100 | 101 | ast = ast 102 | .with_members(ast.members() 103 | .with_methods(ast.members().methods().push(method))) 104 | end 105 | end 106 | 107 | // Extract all Field initialisers as Exprs into a Sequence. 108 | var inits = coll.Vec[Expr] 109 | for field in ast.members().fields().values() do 110 | try 111 | inits = inits.push( 112 | Assign(Reference(field.name()), field.default() as Expr)) 113 | end 114 | end 115 | 116 | // Insert the field initialisers at the start of every constructor. 117 | if inits.size() > 0 then 118 | var methods = coll.Vec[Method] 119 | for method in ast.members().methods().values() do 120 | methods = methods.push( 121 | match method | let _: MethodNew => 122 | method.with_body(Sequence( 123 | try 124 | inits.concat((method.body() as Sequence).list().values()) 125 | else 126 | inits 127 | end)) 128 | else 129 | method 130 | end) 131 | end 132 | ast = ast.with_members(ast.members().with_methods(methods)) 133 | end 134 | 135 | frame.replace(ast) 136 | 137 | elseif A <: MethodNew then 138 | var ast: MethodNew = ast' 139 | 140 | // Apply a default cap to the constructor. 141 | if ast.cap() is None then 142 | match frame.type_decl() 143 | | let _: Primitive => ast = ast.with_cap(Val) 144 | | let _: Actor => ast = ast.with_cap(Tag) 145 | else ast = ast.with_cap(Ref) 146 | end 147 | end 148 | 149 | // Set the return type of the constructor. 150 | match ast.cap() | let cap: Cap => 151 | let type_name = frame.type_decl().name() 152 | ast = ast.with_return_type( 153 | NominalType(type_name where cap' = cap, cap_mod' = Ephemeral)) 154 | else 155 | Unreachable(ast.cap()) 156 | end 157 | 158 | frame.replace(ast) 159 | 160 | elseif A <: MethodBe then 161 | // Set the cap and the return type of the behaviour. 162 | frame.replace(ast' 163 | .with_cap(Tag) 164 | .with_return_type(NominalType(Id("None")))) 165 | 166 | elseif A <: MethodFun then 167 | var ast: MethodFun = ast' 168 | 169 | // Apply a default cap to the function. 170 | if ast.cap() is None then ast = ast.with_cap(Box) end 171 | 172 | // Apply a default return type to the function. 173 | if ast.return_type() is None then 174 | ast = ast.with_return_type(NominalType(Id("None"))) 175 | end 176 | 177 | frame.replace(ast) 178 | 179 | elseif A <: Return then 180 | // Set the return value if none has been specified. 181 | if ast'.value() is None then 182 | try frame.method() as MethodNew 183 | frame.replace(ast'.with_value(This)) 184 | else 185 | frame.replace(ast'.with_value(Reference(Id("None")))) 186 | end 187 | end 188 | 189 | elseif A <: For then 190 | // TODO: actual hygienic id 191 | // TODO: attach "nice name" to Id: "for loop iterator"? 192 | let iter_id = Id("$hygienic_id") 193 | 194 | let try_next = Try( 195 | Sequence([Call(Dot(Reference(iter_id), Id("next")))]), 196 | Sequence([Break]) 197 | ) 198 | 199 | frame.replace(Sequence([ 200 | Assign(LocalLet(iter_id), ast'.iterator()) 201 | While( 202 | Sequence([Call(Dot(Reference(iter_id), Id("has_next")))]), 203 | Sequence([Assign(ast'.refs(), try_next)]) 204 | ) 205 | ])) 206 | 207 | elseif A <: With then 208 | let preamble: Array[Expr] trn = [] 209 | let try_body: Array[Expr] trn = [] 210 | let else_body: Array[Expr] trn = [] 211 | let then_body: Array[Expr] trn = [] 212 | 213 | // Set up the preamble, and the preamble of each body section. 214 | // These contain assignments related to the "with expression", 215 | // and the final section contains calls to the "dispose" method. 216 | for assign in ast'.assigns().elements().values() do 217 | // TODO: actual hygienic id 218 | let hyg_id = Id("$hygienic_id") 219 | let id = 220 | try (assign.left() as Reference).name() 221 | else Unreachable(assign); Id("") 222 | end 223 | 224 | let local_assign = Assign(LocalLet(id), Reference(hyg_id)) 225 | 226 | preamble.push(Assign(LocalLet(hyg_id), assign.right())) 227 | try_body.push(local_assign) 228 | else_body.push(local_assign) 229 | then_body.push(Call(Dot(Reference(hyg_id), Id("dispose")))) 230 | end 231 | 232 | // Add the actual body and else body contents. 233 | for expr in ast'.body().list().values() do 234 | try_body.push(expr) 235 | end 236 | try 237 | for expr in (ast'.else_body() as Sequence).list().values() do 238 | else_body.push(expr) 239 | end 240 | end 241 | 242 | frame.replace(Sequence((consume preamble) .> push( 243 | Try( 244 | Sequence(consume try_body), 245 | Sequence(consume else_body), 246 | Sequence(consume then_body) 247 | ) 248 | ))) 249 | 250 | elseif A <: Match then 251 | var has_changes = false 252 | let cases: Array[Case] trn = [] 253 | 254 | // If any cases have no body, they get the body of the next case. 255 | var next_body: (Sequence | None) = None 256 | for case in ast'.cases().list().reverse().values() do // TODO: use rvalues() 257 | try next_body = case.body() as Sequence end 258 | 259 | cases.unshift( 260 | if case.body() is None then 261 | has_changes = true 262 | case.with_body(next_body) 263 | else 264 | case 265 | end 266 | ) 267 | end 268 | 269 | if has_changes then 270 | frame.replace(ast'.with_cases(Cases(consume cases))) 271 | end 272 | 273 | elseif A <: Assign then 274 | match ast'.left() | let call: Call => 275 | // Sugar as an update method call, with the right side as a named arg. 276 | frame.replace(call 277 | .with_callable(Dot(call.callable(), Id("update"))) 278 | .with_named_args(call.named_args().with_list_push( 279 | NamedArg(Id("value"), Sequence([ast'.right()])) 280 | )) 281 | ) 282 | end 283 | 284 | elseif A <: UnaryOp then 285 | match _unary_op_method_id[A]() | let method_id: Id => 286 | frame.replace(Call(Dot(ast'.expr(), method_id))) 287 | end 288 | 289 | elseif A <: BinaryOp then 290 | match _binary_op_method_id[A]() | let method_id: Id => 291 | let args = 292 | match ast'.right() 293 | | let tuple: Tuple => Args(tuple.elements()) 294 | | let expr: Expr => Args([Sequence([expr])]) 295 | end 296 | 297 | frame.replace( 298 | Call(Dot(ast'.left(), method_id), args, NamedArgs, ast'.partial()) 299 | ) 300 | end 301 | 302 | // TODO: from sugar.c - sugar_ffi 303 | // TODO: from sugar.c - sugar_ifdef 304 | // TODO: from sugar.c - sugar_use 305 | // TODO: from sugar.c - sugar_lambdatype 306 | // TODO: from sugar.c - sugar_barelambda 307 | // TODO: from sugar.c - sugar_location 308 | 309 | end 310 | -------------------------------------------------------------------------------- /ponycc/pass/syntax/syntax.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../../ast" 4 | use "../../frame" 5 | use "../../unreachable" 6 | 7 | primitive Syntax is (Pass[Program, Program] & FrameVisitor[Syntax]) 8 | """ 9 | The purpose of the Syntax pass is to impose some extra limitation on the 10 | forms that are allowed in the Pony compiler, even when they are allowed 11 | by the parser, or by the AST data structure types. 12 | 13 | Checks that require any kind of larger context (such as other packages, 14 | scoping, name resolution, type information) do not belong here. 15 | 16 | This pass only reads the AST. 17 | """ 18 | fun name(): String => "syntax" 19 | 20 | fun apply(ast: Program, fn: {(Program, Array[PassError] val)} val) => 21 | let runner = FrameRunner[Syntax](ast, fn) 22 | 23 | runner.view_each_ffi_decl({(ffi_decl) => 24 | for param in ffi_decl.params().list().values() do 25 | try 26 | runner.err(param.default() as Expr, 27 | "An FFI declaration parameter may not have a default value.") 28 | end 29 | end 30 | }) 31 | 32 | fun visit[A: AST val](frame: Frame[Syntax], ast: A) => 33 | iftype A <: TypeDecl then 34 | // TODO: check that ast.name() is a valid type name (uppercase). 35 | 36 | let desc = 37 | iftype A <: TypeAlias then "A type alias " 38 | elseif A <: Interface then "An interface " 39 | elseif A <: Trait then "A trait " 40 | elseif A <: Primitive then "A primitive " 41 | elseif A <: Struct then "A struct " 42 | elseif A <: Class then "A class " 43 | elseif A <: Actor then "An actor " 44 | else Unreachable(ast); "" 45 | end 46 | 47 | iftype A <: TypeAlias then 48 | try ast.provides() as Type else 49 | frame.err(ast, desc + "must specify a type.") 50 | end 51 | end 52 | 53 | iftype A <: (TypeAlias | Primitive | Actor) then 54 | try 55 | frame.err(ast.cap() as Cap, 56 | desc + "cannot specify a default capability.") 57 | end 58 | end 59 | 60 | iftype 61 | A <: (TypeAlias | Interface | Trait | Primitive | Struct | Class) 62 | then 63 | try 64 | frame.err(ast.at() as At, desc + "cannot specify a C API.") 65 | end 66 | 67 | if ast.name().value() == "Main" then 68 | frame.err(ast.name(), 69 | desc + "cannot be named Main - Main must be an actor.") 70 | end 71 | end 72 | 73 | try ast.at() as At 74 | frame.err(ast.type_params() as TypeParams, 75 | "A C API type cannot have type parameters.") 76 | end 77 | 78 | iftype A <: (TypeAlias | Interface | Trait | Primitive) then 79 | for f in ast.members().fields().values() do 80 | frame.err(f, desc + "cannot have fields.") 81 | end 82 | end 83 | 84 | iftype A <: TypeAlias then 85 | for m in ast.members().methods().values() do 86 | frame.err(m, desc + "cannot have methods.") 87 | end 88 | end 89 | 90 | iftype A <: (Primitive | Struct | Class) then 91 | for m in ast.members().methods().values() do 92 | try 93 | frame.err(m as MethodBe, desc + "cannot have behaviours.") 94 | end 95 | end 96 | end 97 | 98 | // TODO: syntax.c: check_provides_type 99 | // but this should probably be in the Traits pass instead. 100 | 101 | elseif A <: Method then 102 | // TODO: check that ast.name() is a valid method name (lowercase). 103 | 104 | iftype A <: MethodFun then 105 | try frame.type_decl() as (Trait | Interface) else 106 | try 107 | frame.err(ast.body() as None; ast, 108 | "This function must provide a body.") 109 | end 110 | end 111 | else 112 | try 113 | frame.err(ast.return_type() as Type, 114 | "Only functions can specify a return type.") 115 | end 116 | 117 | try 118 | frame.err(ast.cap() as At, 119 | "Only functions can be bare.") 120 | end 121 | end 122 | 123 | iftype A <: MethodNew then 124 | try frame.type_decl() as Primitive 125 | frame.err(ast.cap() as Cap, 126 | "A primitive constructor cannot specify a receiver capability.") 127 | end 128 | 129 | try frame.type_decl() as Actor 130 | frame.err(ast.partial() as Question, 131 | "An actor constructor cannot be partial.") 132 | end 133 | 134 | try frame.type_decl() as (Trait | Interface) 135 | try 136 | frame.err(ast.body() as Sequence, 137 | "A trait or interface constructor cannot provide a body.") 138 | end 139 | else 140 | try 141 | frame.err(ast.body() as None; ast, 142 | "This constructor must provide a body.") 143 | end 144 | end 145 | end 146 | 147 | iftype A <: MethodBe then 148 | try 149 | frame.err(ast.cap() as Cap, 150 | "A behaviour cannot specify a receiver capability.") 151 | end 152 | 153 | try 154 | frame.err(ast.partial() as Question, 155 | "A behaviour cannot be partial.") 156 | end 157 | 158 | try frame.type_decl() as (Trait | Interface) else 159 | try 160 | frame.err(ast.body() as None; ast, 161 | "This behaviour must provide a body.") 162 | end 163 | end 164 | end 165 | 166 | try 167 | frame.err(ast.guard() as Sequence, 168 | "Method guards are not yet supported in this compiler.") 169 | end 170 | 171 | elseif A <: Field then 172 | // TODO: check that ast.name() is a valid field name (lowercase). 173 | 174 | // TODO: if in an Object, require that fields be initialized, 175 | // though maybe this logic belongs in another pass. 176 | 177 | None 178 | 179 | elseif A <: Local then 180 | // TODO: check that ast.name() is a valid local name (lowercase). 181 | 182 | None 183 | 184 | elseif A <: Param then 185 | // TODO: check that ast.name() is a valid param name (lowercase). 186 | 187 | None 188 | 189 | elseif A <: Params then 190 | try 191 | frame.err(ast.ellipsis() as Ellipsis, 192 | "An ellipsis may only appear in FFI declaration parameters.") 193 | end 194 | 195 | elseif A <: CallFFI then 196 | for named in ast.named_args().list().values() do 197 | frame.err(named, "An FFI call may not have named arguments.") 198 | end 199 | 200 | // TODO: from syntax.c - syntax_object 201 | 202 | elseif A <: LambdaCapture then 203 | if ast.value() is None then 204 | try 205 | frame.err(ast.local_type() as Type, 206 | "A lambda capture cannot specify a type without a value.") 207 | end 208 | end 209 | 210 | elseif A <: BareLambda then 211 | try 212 | frame.err(ast.method_cap() as Cap, 213 | "A bare lambda cannot specify a receiver capability.") 214 | end 215 | 216 | try 217 | frame.err(ast.type_params() as TypeParams, 218 | "A bare lambda cannot have type parameters.") 219 | end 220 | 221 | try 222 | frame.err(ast.captures() as LambdaCaptures, 223 | "A bare lambda cannot have captures.") 224 | end 225 | 226 | try 227 | frame.err(ast.object_cap() as (Iso | Trn | Ref | Box | Tag), 228 | "A bare lambda can only have the `val` capability.") 229 | end 230 | 231 | // TODO: from syntax.c - syntax_lambda (BareLambda only) 232 | // TODO: from syntax.c - syntax_barelambdatype 233 | 234 | elseif A <: Cases then 235 | for (idx, case) in ast.list().pairs() do 236 | if (idx >= (ast.list().size() - 1)) and (case.body() is None) then 237 | frame.err(case, "The final case in a match block must have a body.") 238 | end 239 | end 240 | 241 | elseif A <: Return then 242 | if frame.method_body() is None then 243 | frame.err(ast, 244 | "A return statement can only appear in a method body.") 245 | end 246 | 247 | try frame.method() as MethodNew 248 | frame.err(ast.value() as Expr, 249 | "A return statement in a constructor cannot have a value expression.") 250 | end 251 | 252 | try frame.method() as MethodBe 253 | frame.err(ast.value() as Expr, 254 | "A return statement in a behaviour cannot have a value expression.") 255 | end 256 | 257 | elseif A <: Error then 258 | try 259 | frame.err(ast.value() as Expr, 260 | "An error statement cannot have a value expression.") 261 | end 262 | 263 | elseif A <: CompileIntrinsic then 264 | try 265 | let body = frame.method_body() as Sequence 266 | if not (body.list().size() == 1) then error end 267 | if not (body.list()(0)? is ast) then error end 268 | else 269 | frame.err(ast, "A compile intrinsic must be the entire method body.") 270 | end 271 | 272 | try 273 | frame.err(ast.value() as Expr, 274 | "A compile intrinsic cannot have a value expression.") 275 | end 276 | 277 | elseif A <: CompileError then 278 | try 279 | let body = (frame.parent(2) as IfDef).then_body() 280 | if not ((body.list().size() == 1) and (body.list()(0)? is ast)) then 281 | frame.err(ast, 282 | "A compile error must be the entire ifdef clause body.") 283 | end 284 | else 285 | frame.err(ast, "A compile error must be in an ifdef clause.") 286 | end 287 | 288 | try ast.value() as LitString else 289 | frame.err(try ast.value() as Expr else ast end, 290 | "A compile error must have a string value explaining the error.") 291 | end 292 | 293 | // TODO: from syntax.c - syntax_nominal 294 | 295 | elseif A <: ThisType then 296 | try frame.parent() as ViewpointType else 297 | frame.err(ast, "`this` can only be used in a type as a viewpoint.") 298 | end 299 | 300 | elseif A <: ViewpointType then 301 | if 302 | (frame.constraint() isnt None) and 303 | (frame.iftype_constraint() isnt None) 304 | then 305 | frame.err(ast, "A viewpoint type cannot be used as a constraint.") 306 | end 307 | 308 | try 309 | frame.err(ast.right() as ThisType, 310 | "`this` cannot appear on the right side of a viewpoint type.") 311 | end 312 | 313 | try 314 | frame.err(ast.right() as Cap, 315 | "A reference capability cannot appear on the right side of a " + 316 | "viewpoint type.") 317 | end 318 | 319 | elseif A <: TupleType then 320 | if frame.constraint() isnt None then 321 | frame.err(ast, "A tuple cannot be used as a type parameter constraint.") 322 | end 323 | 324 | elseif A <: BareLambdaType then 325 | try 326 | frame.err(ast.method_cap() as Cap, 327 | "A bare lambda type cannot specify a receiver capability.") 328 | end 329 | 330 | try 331 | frame.err(ast.type_params() as TypeParams, 332 | "A bare lambda type cannot have type parameters.") 333 | end 334 | 335 | try 336 | frame.err(ast.object_cap() as (Iso | Trn | Ref | Box | Tag), 337 | "A bare lambda type can only have the `val` capability.") 338 | end 339 | 340 | elseif A <: TypeParam then 341 | // TODO: check that ast.name() is a valid type param name (uppercase). 342 | 343 | None 344 | 345 | elseif A <: (Cap | GenCap) then 346 | if 347 | match frame.parent() 348 | | let t: (TypeDecl | Object) => t.cap() isnt ast 349 | | let m: Method => m.cap() isnt ast 350 | | let c: (Consume | Recover) => c.cap() isnt ast 351 | | let n: NominalType => n.cap() isnt ast 352 | | let v: ViewpointType => v.left() isnt ast 353 | | let l: ( Lambda 354 | | LambdaType 355 | | BareLambda 356 | | BareLambdaType) => (l.method_cap() isnt ast) and 357 | (l.object_cap() isnt ast) 358 | else true 359 | end 360 | then 361 | frame.err(ast, "A type cannot be only a reference capability.") 362 | end 363 | 364 | iftype A <: GenCap then 365 | if 366 | (frame.constraint() is None) and (frame.iftype_constraint() is None) 367 | then 368 | frame.err(ast, 369 | "A reference capability set can only appear in a constraint.") 370 | end 371 | end 372 | 373 | elseif A <: Case then 374 | let pattern_exprs = [ast.expr()] 375 | 376 | try while true do 377 | match pattern_exprs.shift()? 378 | | let local: LocalVar => 379 | frame.err(local, 380 | "A match capture cannot be declared as `var`; use `let` instead.") 381 | | let local: LocalLet => 382 | match local.local_type() 383 | | None => 384 | frame.err(local, 385 | "The type of a match capture must be explicitly declared.") 386 | | let tuple_type: TupleType => 387 | frame.err(tuple_type, 388 | "A match cannot capture a tuple; use a tuple of match " + 389 | "captures instead.") 390 | end 391 | | let tuple: Tuple => 392 | for seq in tuple.elements().values() do 393 | pattern_exprs.push(seq) 394 | end 395 | | let seq: Sequence => 396 | if seq.list().size() > 1 then 397 | frame.err(seq, 398 | "A sequence inside a match capture must have only one element.") 399 | else 400 | try pattern_exprs.push(seq.list()(0)?) end 401 | end 402 | end 403 | end end 404 | end 405 | -------------------------------------------------------------------------------- /ponycc/polyfill/sort.pony: -------------------------------------------------------------------------------- 1 | /// 2 | // This source code is copied from ponyc, and modified to suit our needs. 3 | // The intention is to eventually create an RFC to modify it upstream. 4 | // 5 | // Copyright (C) 2016-2017, The Pony Developers 6 | // Copyright (c) 2014-2015, Causality Ltd. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // 12 | // 1. Redistributions of source code must retain the above copyright notice, this 13 | // list of conditions and the following disclaimer. 14 | // 2. Redistributions in binary form must reproduce the above copyright notice, 15 | // this list of conditions and the following disclaimer in the documentation 16 | // and/or other materials provided with the distribution. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | interface _TransformFn[A, B] 30 | new val create() 31 | fun apply(a: A): B 32 | 33 | primitive _IdentityFn[A] is _TransformFn[A, A] 34 | fun apply(a: A): A => a 35 | 36 | type Sort[A: Comparable[A] #read] is SortBy[A, A, _IdentityFn[A]] 37 | 38 | primitive SortBy[A: Any #alias, B: Comparable[B] #read, F: _TransformFn[A, B]] 39 | """ 40 | Implementation of dual-pivot quicksort. 41 | """ 42 | fun apply(seq: Seq[A]) => 43 | """ 44 | Sort the given seq. 45 | """ 46 | try _sort(seq, 0, seq.size().isize() - 1)? end 47 | 48 | fun _sort(seq: Seq[A], lo: ISize, hi: ISize) ? => 49 | if hi <= lo then return end 50 | // choose outermost elements as pivots 51 | if F(seq(lo.usize())?) > F(seq(hi.usize())?) then _swap(seq, lo, hi)? end 52 | (var p, var q) = (F(seq(lo.usize())?), F(seq(hi.usize())?)) 53 | // partition according to invariant 54 | (var l, var g) = (lo + 1, hi - 1) 55 | var k = l 56 | while k <= g do 57 | if F(seq(k.usize())?) < p then 58 | _swap(seq, k, l)? 59 | l = l + 1 60 | elseif F(seq(k.usize())?) >= q then 61 | while (F(seq(g.usize())?) > q) and (k < g) do g = g - 1 end 62 | _swap(seq, k, g)? 63 | g = g - 1 64 | if F(seq(k.usize())?) < p then 65 | _swap(seq, k, l)? 66 | l = l + 1 67 | end 68 | end 69 | k = k + 1 70 | end 71 | (l, g) = (l - 1, g + 1) 72 | // swap pivots to final positions 73 | _swap(seq, lo, l)? 74 | _swap(seq, hi, g)? 75 | // recursively sort 3 partitions 76 | _sort(seq, lo, l - 1)? 77 | _sort(seq, l + 1, g - 1)? 78 | _sort(seq, g + 1, hi)? 79 | 80 | fun _swap(seq: Seq[A], i: ISize, j: ISize) ? => 81 | seq(j.usize())? = seq(i.usize())? = seq(j.usize())? 82 | -------------------------------------------------------------------------------- /ponycc/resolve_source_files.pony: -------------------------------------------------------------------------------- 1 | 2 | use "files" 3 | use "glob" 4 | use "ast" 5 | 6 | class val ResolveSourceFiles 7 | """ 8 | This class is responsible for finding and loading *.pony source files. 9 | """ 10 | let _auth: AmbientAuth 11 | let _search_paths: Array[String] val 12 | 13 | new val create(auth': AmbientAuth, search_paths': Array[String] val = []) => 14 | """ 15 | Ambient authority is required so that the entire filesystem is available. 16 | 17 | The optional search_paths parameter will specify the global search paths 18 | to fall back to if a directory couldn't be found relative to the start path. 19 | """ 20 | (_auth, _search_paths) = (auth', search_paths') 21 | 22 | fun apply(start_path: String, path: String): (String, Sources)? => 23 | """ 24 | Given the path of a file or directory to start with, find a directory 25 | that exists relative to that starting path or one of the other search paths, 26 | and return all of the *.pony source files that exist in that directory, 27 | loaded into memory as Source objects, ready for parsing. 28 | 29 | If the start_path is not a directory, the directory containing that file 30 | will be used as the actual starting path for the search. 31 | """ 32 | let dir_path = _find_dir(start_path, path)? 33 | let sources: Array[Source] trn = [] 34 | for file_path in Glob.glob(dir_path, "*.pony").values() do 35 | let file = File.open(file_path) 36 | if (file.errno() is FileOK) and (file.size() != -1) then 37 | let content = String.from_array(file.read(file.size())) 38 | sources.push(Source(content, file_path.path)) 39 | end 40 | end 41 | (dir_path.path, consume sources) 42 | 43 | fun _find_dir(start_path: String, path: String): FilePath? => 44 | """ 45 | Given the path of a file or directory to start with, find a directory 46 | that exists relative to that starting path or one of the other search paths. 47 | 48 | If the start_path is not a directory, the directory containing that file 49 | will be used as the actual starting path for the search. 50 | """ 51 | let all_search_paths = [start_path] .> concat(_search_paths.values()) 52 | for search_path in all_search_paths.values() do 53 | try 54 | let dir_path = FilePath(_auth, Path.abs( 55 | if _is_dir(search_path) then 56 | Path.join(search_path, path) 57 | else 58 | Path.join(Path.dir(search_path), path) 59 | end 60 | ))? 61 | 62 | if _is_dir(dir_path.path) then return dir_path end 63 | else 64 | continue 65 | end 66 | end 67 | 68 | error 69 | 70 | fun _is_dir(path: String): Bool => 71 | """ 72 | Return true if the given path exists as a directory on the file system. 73 | """ 74 | try FileInfo(FilePath(_auth, path)?)?.directory else false end 75 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/names_example.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $NAMES 3 | $ERROR This is a referenced type. 4 | primitive X 5 | ^ 6 | $ERROR This is a reference. 7 | fun apply(): X => X 8 | ^ 9 | $ERROR This is a reference. 10 | fun bogus(): Y => Y 11 | ^ 12 | """ 13 | 14 | primitive X 15 | 16 | primitive P 17 | fun apply(): X => X 18 | fun bogus(): Y => Y 19 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_binary_op.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $ERROR Operator precedence is not supported. Parentheses are required. 4 | fun apple(): U64 => 1 + 2 * 3 + 4 + 5 5 | ^ 6 | $ERROR Operator precedence is not supported. Parentheses are required. 7 | fun apple(): U64 => 1 + 2 * 3 + 4 + 5 8 | ^ 9 | $ERROR Operator precedence is not supported. Parentheses are required. 10 | fun currant(): U64 => false or false or true and false or true 11 | ^~~ 12 | $ERROR Operator precedence is not supported. Parentheses are required. 13 | fun currant(): U64 => false or false or true and false or true 14 | ^~ 15 | """ 16 | 17 | trait T 18 | fun apple(): U64 => 1 + 2 * 3 + 4 + 5 19 | fun banana(): U64 => 1 + (2 * 3) + 4 + 5 20 | fun currant(): U64 => false or false or true and false or true 21 | fun dewberry(): Bool => false or ((false or true) and false) or true 22 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_docs.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $ERROR A method with a body cannot have a docstring in the signature. 4 | fun apple(): Bool "one bad docstring" => true 5 | ^~~~~~~~~~~~~~~~~~~ 6 | $ERROR A method with a body cannot have a docstring in the signature. 7 | fun banana(): Bool "another bad one" => "the real docstring"; true 8 | ^~~~~~~~~~~~~~~~~ 9 | $CHECK type_decls-0.members.methods-2.docs 10 | LitString(this docstring is okay) 11 | 12 | $CHECK type_decls-0.members.methods-2.body 13 | None 14 | 15 | $CHECK type_decls-0.members.methods-3.docs 16 | LitString(this one is also fine) 17 | 18 | $CHECK type_decls-0.members.methods-3.body 19 | Sequence([LitTrue]) 20 | 21 | """ 22 | 23 | trait T 24 | fun apple(): Bool "one bad docstring" => true 25 | fun banana(): Bool "another bad one" => "the real docstring"; true 26 | fun currant(): Bool "this docstring is okay" 27 | fun dewberry(): Bool => "this one is also fine"; true 28 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_error_empty_use.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $ERROR Parse error: Expected package or ffi declaration after Use 4 | use 5 | ^~~ 6 | """ 7 | 8 | use 9 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_error_unterminated_lambda.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $ERROR Parse error: Unterminated lambda expression 4 | {(n: U64): U64 => n - 1 5 | ^ 6 | $ERROR Expected terminating lambda expression before here 7 | end 8 | ^~~ 9 | """ 10 | 11 | actor Main 12 | new create(env: Env) => 13 | if true then 14 | {(n: U64): U64 => n - 1 15 | end 16 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_semicolon.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $ERROR Use semicolons only for separating expressions on the same line. 4 | 3;;; 5 | ^ 6 | $ERROR Use semicolons only for separating expressions on the same line. 7 | 4;; 8 | ^ 9 | $ERROR Use semicolons only for separating expressions on the same line. 10 | 5; 11 | ^ 12 | """ 13 | 14 | primitive P 15 | fun apply() => 16 | 0; 1; 2 17 | 3;;; 18 | 4;; 19 | 5; 20 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/parse_type.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PARSE 3 | $CHECK type_decls-0.members.methods-0.return_type 4 | NominalType(Id(T), None, None, None, None) 5 | 6 | $CHECK type_decls-0.members.methods-1.return_type 7 | NominalType(Id(T), None, None, None, None) 8 | 9 | $CHECK type_decls-0.members.methods-2.return_type 10 | TupleType([NominalType(Id(T), None, None, None, None); 11 | NominalType(Id(Bool), None, None, None, None)]) 12 | 13 | """ 14 | 15 | trait T 16 | fun single_return(): T => this 17 | fun single_tuple_return(): (T) => this 18 | fun multi_tuple_return(): (T, Bool) => (this, true) 19 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_ffi.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply() => 7 | @system[I32]("echo Hello World!".cstring()) 8 | @"partial function"[None](where risky = true)? 9 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_if.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun test_if(a: Bool, b: Bool, c: Bool) => 7 | if a then 8 | A 9 | A 10 | end 11 | if a then 12 | A 13 | A 14 | else 15 | None 16 | None 17 | end 18 | if a then 19 | A 20 | A 21 | elseif b then 22 | B 23 | B 24 | elseif c then 25 | C 26 | C 27 | end 28 | if a then 29 | A 30 | A 31 | elseif b then 32 | B 33 | B 34 | else 35 | None 36 | None 37 | end 38 | 39 | fun test_ifdef() => 40 | ifdef "custom" then 41 | A 42 | A 43 | end 44 | ifdef linux then 45 | A 46 | A 47 | else 48 | compile_error 49 | compile_error "message" 50 | end 51 | ifdef linux then 52 | A 53 | A 54 | elseif osx then 55 | B 56 | B 57 | elseif windows then 58 | C 59 | C 60 | end 61 | ifdef linux then 62 | A 63 | A 64 | elseif osx then 65 | B 66 | B 67 | else 68 | compile_intrinsic 69 | compile_intrinsic None 70 | end 71 | 72 | fun test_iftype[X: (A | B | C)]() => 73 | iftype X <: A then 74 | A 75 | A 76 | end 77 | iftype X <: A then 78 | A 79 | A 80 | else 81 | None 82 | None 83 | end 84 | iftype X <: A then 85 | A 86 | A 87 | elseif X <: B then 88 | B 89 | B 90 | elseif X <: C then 91 | C 92 | C 93 | end 94 | iftype X <: A then 95 | A 96 | A 97 | elseif X <: B then 98 | B 99 | B 100 | else 101 | None 102 | None 103 | end 104 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_lambda.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | class C 6 | let e: {()} 7 | let f: {(I32, I32): String ?} 8 | let g: {val foo(I32, I32): String} val 9 | 10 | fun apply() => 11 | e = {() => 12 | None 13 | } 14 | f = {(a: I32, b: I32): String ? => 15 | error 16 | } 17 | g = {val foo(a: I32, b: I32)(e): String => 18 | "" 19 | } val 20 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_literal.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun test_simple() => 7 | this 8 | true 9 | false 10 | 88 11 | 88.8 12 | 8e8 13 | "foo" 14 | 'f' 15 | __loc 16 | 17 | fun test_array() => 18 | [ 19 | A 20 | B 21 | C 22 | ] 23 | [as T: 24 | A 25 | B 26 | C 27 | ] 28 | 29 | fun test_tuple() => 30 | (A, B, C) 31 | ((A, B), C) 32 | (A, (B, C)) 33 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_local.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply() => 7 | let bison = "bison" 8 | let llama: String = "llama" 9 | var integer: U64 = 88 10 | var decimal = F64(88) 11 | integer = 99 12 | decimal = 99 13 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_loop.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun test_while() => 7 | while true do 8 | break 9 | break A 10 | end 11 | while false do 12 | continue 13 | continue A 14 | else 15 | None 16 | None 17 | end 18 | 19 | fun test_repeat() => 20 | repeat 21 | return 22 | return A 23 | until true 24 | end 25 | repeat 26 | return 27 | return A 28 | until false else 29 | None 30 | None 31 | end 32 | 33 | fun test_for(array: Array[A]) => 34 | for a in array.values() do 35 | A 36 | A 37 | end 38 | for (i, a) in array.pairs() do 39 | A 40 | A 41 | else 42 | None 43 | None 44 | end 45 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_match.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply(x: (A | B | C)) => 7 | match x 8 | | let a: A => 9 | a 10 | a 11 | | let b: B => 12 | b 13 | b 14 | | let c: C if c < 0 => 15 | c 16 | c 17 | else 18 | None 19 | None 20 | end 21 | match x 22 | | A | B | C => 23 | None 24 | end 25 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_object.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply(): T => 7 | object 8 | end 9 | object val is T 10 | let x: X = X 11 | 12 | fun apply(): this->X => 13 | x 14 | end 15 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_operator.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun test_arithmetic() => 7 | 7 + 8 + 9 8 | (7 + 8) + 9 9 | 7 - 8 - 9 10 | 7 - (8 - 9) 11 | 7 * 8 * 9 12 | (7 * 8) * 9 13 | 7 / 8 / 9 14 | 7 / (8 / 9) 15 | 7 >> 8 >> 9 16 | (7 >> 8) >> 9 17 | 7 << 8 << 9 18 | 7 << (8 << 9) 19 | -7 + 8 + 9 20 | -(7 + 8) + 9 21 | -(7 + 8 + 9) 22 | 23 | fun test_arithmetic_unsafe() => 24 | 7 +~ 8 +~ 9 25 | (7 +~ 8) +~ 9 26 | 7 -~ 8 -~ 9 27 | 7 -~ (8 -~ 9) 28 | 7 *~ 8 *~ 9 29 | (7 *~ 8) *~ 9 30 | 7 /~ 8 /~ 9 31 | 7 /~ (8 /~ 9) 32 | 7 >>~ 8 >>~ 9 33 | (7 >>~ 8) >>~ 9 34 | 7 <<~ 8 <<~ 9 35 | 7 <<~ (8 <<~ 9) 36 | -~7 + 8 + 9 37 | -~(7 + 8) + 9 38 | -~(7 + 8 + 9) 39 | 40 | fun test_comparison(a: A, b: B, c: C) => 41 | a == b == c 42 | (a == b) == c 43 | a != b != c 44 | a != (b != c) 45 | a >= b >= c 46 | (a >= b) >= c 47 | a <= b <= c 48 | a <= (b <= c) 49 | a > b > c 50 | (a > b) > c 51 | a < b < c 52 | a < (b < c) 53 | 54 | fun test_comparison_unsafe(a: A, b: B, c: C) => 55 | a ==~ b ==~ c 56 | (a ==~ b) ==~ c 57 | a !=~ b !=~ c 58 | a !=~ (b !=~ c) 59 | a >=~ b >=~ c 60 | (a >=~ b) >=~ c 61 | a <=~ b <=~ c 62 | a <=~ (b <=~ c) 63 | a >~ b >~ c 64 | (a >~ b) >~ c 65 | a <~ b <~ c 66 | a <~ (b <~ c) 67 | 68 | fun test_logical(a: Bool, b: Bool, c: Bool) => 69 | a and b and c 70 | a and (b or c) 71 | a or b or c 72 | (a or b) and c 73 | a xor b xor c 74 | (not a) and (not not b) and c 75 | not (a and (not not (b and c))) 76 | 77 | fun test_identity(a: A, b: B, c: C) => 78 | a is b is c 79 | (a is b) is c 80 | a isnt b isnt c 81 | a isnt (b isnt c) 82 | (addressof a) is (addressof addressof b) is c 83 | addressof (a is (addressof addressof (b is c))) 84 | (digestof a) is (digestof digestof b) is c 85 | digestof (a is (digestof digestof (b is c))) 86 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_postfix.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply() => 7 | foo(A, B, C) 8 | foo(where a = A, b = B, c = C) 9 | foo(A, B where c = C) 10 | foo[A, B, C](A, B where c = C) 11 | this~foo[A, B, C](A, B where c = C) 12 | this.foo[A, B, C](A, B where c = C) 13 | this.>foo[A, B, C](A, B where c = C) 14 | this~foo[A, B, C](A, B where c = C)~bar[A, B, C](A, B where c = C) 15 | this.foo[A, B, C](A, B where c = C).bar[A, B, C](A, B where c = C) 16 | this.>foo[A, B, C](A, B where c = C).>bar[A, B, C](A, B where c = C) 17 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_recover.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply(x: A) => 7 | recover 8 | A(consume x) 9 | end 10 | recover val 11 | A(consume val x) 12 | end 13 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_try.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply(x: (A | B | C)) => 7 | try 8 | x as A 9 | end 10 | try 11 | partial() 12 | else 13 | None 14 | then 15 | None 16 | end 17 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_type.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | type PCII is P[C[I, I]] 6 | 7 | interface val I 8 | fun apply(a: I32, b: I32): I32 ? 9 | """ 10 | This is the docstring for a method that has no body, in a test fixture. 11 | """ 12 | 13 | trait ref T 14 | 15 | primitive P[A: T] 16 | 17 | struct S 18 | """ 19 | This is the docstring for a struct that is used in a test fixture. 20 | """ 21 | var a: I32 = 0 22 | var b: I32 = 0 23 | 24 | class C[A, B = I] is (T & I) 25 | """ 26 | This is the docstring for a class that is used in a test fixture. 27 | """ 28 | 29 | actor Main 30 | let _env: Env 31 | embed array: Array[String] = [] 32 | 33 | new create(env: Env) => 34 | _env = env 35 | 36 | be awesome() => 37 | None 38 | 39 | fun tag name[A: T = C[I]](i: I32): String if i == 0 => 40 | "ZERO" 41 | 42 | fun tag name[A: T = C[I]](i: I32): String => 43 | "NONZERO" 44 | 45 | fun fail() ? => 46 | error 47 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_use.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | use "collections" 6 | use time = "time" 7 | use @pony_os_errno[I32]() 8 | use @system[I32](command: Pointer[U8] tag) if posix 9 | use @printf[I32](fmt: Pointer[U8] tag, ...) 10 | use @read[ISize](fd: I32, buffer: Pointer[None], size: USize) if not windows 11 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/print_with.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $PRINT 3 | """ 4 | 5 | primitive P 6 | fun apply() => 7 | with a = A do 8 | a 9 | else 10 | None 11 | None 12 | end 13 | with a = A, b = B, c = C do 14 | a 15 | b 16 | c 17 | end 18 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_assign.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.members.methods-0.body.list-0 4 | Call(Dot(Reference(Id(x)), Id(update)), Args([Sequence([LitInteger(0)])]), 5 | NamedArgs([NamedArg(Id(value), Sequence([LitTrue]))]), None) 6 | 7 | """ 8 | 9 | primitive P 10 | fun apply() => 11 | x(0) = true 12 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_binary_op.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.members.methods-0.body.list-0 4 | Call(Dot(Call(Dot(LitInteger(1), Id(add)), Args([Sequence([LitInteger(2)])]), 5 | NamedArgs([]), None), Id(add)), Args([Sequence([LitInteger(3)])]), 6 | NamedArgs([]), None) 7 | 8 | """ 9 | 10 | primitive P 11 | fun apply() => 12 | 1 + 2 + 3 13 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_jump.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.members.methods-0.body.list-0 4 | Return(This) 5 | $CHECK type_decls-0.members.methods-1.body.list-0 6 | Return(Reference(Id(None))) 7 | $CHECK type_decls-0.members.methods-2.body.list-0 8 | Return(LitTrue) 9 | 10 | """ 11 | 12 | primitive P 13 | new create() => return 14 | fun apply() => return 15 | fun explicit(): Bool => return true 16 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_match.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.members.methods-0.body.list-0.cases.list-0 4 | Case(LitInteger(0), None, Sequence([LitTrue])) 5 | $CHECK type_decls-0.members.methods-0.body.list-0.cases.list-1 6 | Case(LitInteger(1), None, Sequence([LitTrue])) 7 | $CHECK type_decls-0.members.methods-0.body.list-0.cases.list-2 8 | Case(LitInteger(2), None, Sequence([LitTrue])) 9 | $CHECK type_decls-0.members.methods-0.body.list-0.cases.list-3 10 | Case(LitInteger(3), None, Sequence([LitFalse])) 11 | $CHECK type_decls-0.members.methods-0.body.list-0.cases.list-4 12 | Case(LitInteger(4), None, Sequence([LitFalse])) 13 | 14 | """ 15 | 16 | primitive P 17 | fun apply() => 18 | match x 19 | | 0 | 1 | 2 => true 20 | | 3 | 4 => false 21 | end 22 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_type_decl.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.cap 4 | Ref 5 | $CHECK type_decls-1.cap 6 | Box 7 | $CHECK type_decls-2.cap 8 | Ref 9 | $CHECK type_decls-3.cap 10 | Box 11 | $CHECK type_decls-4.cap 12 | Val 13 | $CHECK type_decls-5.cap 14 | Ref 15 | $CHECK type_decls-6.cap 16 | Box 17 | $CHECK type_decls-7.cap 18 | Ref 19 | $CHECK type_decls-8.cap 20 | Box 21 | $CHECK type_decls-9.cap 22 | Tag 23 | 24 | $CHECK type_decls-0.members 25 | Members([], []) 26 | $CHECK type_decls-2.members 27 | Members([], []) 28 | $CHECK type_decls-4.members 29 | Members([], [MethodNew(Id(create), Val, None, Params([], None), 30 | None, None, None, Sequence([LitTrue]), None)]) 31 | $CHECK type_decls-5.members 32 | Members([], [MethodNew(Id(create), Iso, None, Params([], None), 33 | None, None, None, Sequence([LitTrue]), None)]) 34 | $CHECK type_decls-7.members 35 | Members([], [MethodNew(Id(create), Iso, None, Params([], None), 36 | None, None, None, Sequence([LitTrue]), None)]) 37 | $CHECK type_decls-9.members 38 | Members([], [MethodNew(Id(create), Tag, None, Params([], None), 39 | None, None, None, Sequence([LitTrue]), None)]) 40 | 41 | $CHECK type_decls-10.members.methods-0 42 | MethodNew(Id(create), Val, None, Params([], None), 43 | NominalType(Id(PrimitiveWithMembers), None, None, Val, Ephemeral), 44 | None, None, Sequence([LitFalse]), None) 45 | $CHECK type_decls-10.members.methods-1 46 | MethodNew(Id(other_new), Val, None, Params([], None), 47 | NominalType(Id(PrimitiveWithMembers), None, None, Val, Ephemeral), 48 | None, None, Sequence([LitFalse]), None) 49 | $CHECK type_decls-10.members.methods-2 50 | MethodFun(Id(other_fun), Box, None, Params([], None), 51 | NominalType(Id(None), None, None, None, None), 52 | None, None, Sequence([LitFalse]), None) 53 | 54 | $CHECK type_decls-11.members.methods-0 55 | MethodNew(Id(create), Ref, None, Params([], None), 56 | NominalType(Id(StructWithMembers), None, None, Ref, Ephemeral), 57 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 58 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 59 | $CHECK type_decls-11.members.methods-1 60 | MethodNew(Id(other_new), Ref, None, Params([], None), 61 | NominalType(Id(StructWithMembers), None, None, Ref, Ephemeral), 62 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 63 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 64 | $CHECK type_decls-11.members.methods-2 65 | MethodFun(Id(other_fun), Box, None, Params([], None), 66 | NominalType(Id(None), None, None, None, None), 67 | None, None, Sequence([LitFalse]), None) 68 | 69 | $CHECK type_decls-12.members.methods-0 70 | MethodNew(Id(create), Ref, None, Params([], None), 71 | NominalType(Id(ClassWithMembers), None, None, Ref, Ephemeral), 72 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 73 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 74 | $CHECK type_decls-12.members.methods-1 75 | MethodNew(Id(other_new), Ref, None, Params([], None), 76 | NominalType(Id(ClassWithMembers), None, None, Ref, Ephemeral), 77 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 78 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 79 | $CHECK type_decls-12.members.methods-2 80 | MethodFun(Id(other_fun), Box, None, Params([], None), 81 | NominalType(Id(None), None, None, None, None), 82 | None, None, Sequence([LitFalse]), None) 83 | 84 | $CHECK type_decls-13.members.methods-0 85 | MethodNew(Id(create), Tag, None, Params([], None), 86 | NominalType(Id(ActorWithMembers), None, None, Tag, Ephemeral), 87 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 88 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 89 | $CHECK type_decls-13.members.methods-1 90 | MethodNew(Id(other_new), Tag, None, Params([], None), 91 | NominalType(Id(ActorWithMembers), None, None, Tag, Ephemeral), 92 | None, None, Sequence([Assign(Reference(Id(a)), LitTrue); 93 | Assign(Reference(Id(b)), LitFalse); LitFalse]), None) 94 | $CHECK type_decls-13.members.methods-2 95 | MethodFun(Id(other_fun), Box, None, Params([], None), 96 | NominalType(Id(None), None, None, None, None), 97 | None, None, Sequence([LitFalse]), None) 98 | 99 | """ 100 | 101 | interface Interface 102 | interface box InterfaceBox 103 | 104 | trait Trait 105 | trait box TraitBox 106 | 107 | primitive Primitive 108 | 109 | struct Struct 110 | struct box StructBox 111 | 112 | class Class 113 | class box ClassBox 114 | 115 | actor Actor 116 | 117 | primitive PrimitiveWithMembers 118 | new create() => false 119 | new other_new() => false 120 | fun other_fun() => false 121 | 122 | struct StructWithMembers 123 | let a: A = true 124 | var b: B = false 125 | embed c: C 126 | new create() => false 127 | new other_new() => false 128 | fun other_fun() => false 129 | 130 | class ClassWithMembers 131 | let a: A = true 132 | var b: B = false 133 | embed c: C 134 | new create() => false 135 | new other_new() => false 136 | fun other_fun() => false 137 | 138 | actor ActorWithMembers 139 | let a: A = true 140 | var b: B = false 141 | embed c: C 142 | new create() => false 143 | new other_new() => false 144 | fun other_fun() => false 145 | be other_be() => false 146 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/sugar_unary_op.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SUGAR 3 | $CHECK type_decls-0.members.methods-0.body.list-0 4 | Call(Dot(Call(Dot(LitTrue, Id(op_not)), Args([]), NamedArgs([]), None), 5 | Id(op_not)), Args([]), NamedArgs([]), None) 6 | 7 | """ 8 | 9 | primitive P 10 | fun apply() => 11 | not not true 12 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_ffi.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR An FFI declaration parameter may not have a default value. 4 | use @ffi_with_default_params[I32](a: U8 = 0, b: U8 = 1) 5 | ^ 6 | $ERROR An FFI declaration parameter may not have a default value. 7 | use @ffi_with_default_params[I32](a: U8 = 0, b: U8 = 1) 8 | ^ 9 | $ERROR An ellipsis may only appear in FFI declaration parameters. 10 | fun method_with_ellipsis(m: String, ...) => None 11 | ^~~ 12 | $ERROR An FFI call may not have named arguments. 13 | @printf("foo".cstring() where okay = false) 14 | ^~~~ 15 | """ 16 | 17 | use @printf[I32](m: Pointer[U8] tag, ...) 18 | use @ffi_with_default_params[I32](a: U8 = 0, b: U8 = 1) 19 | 20 | primitive P 21 | fun method_with_ellipsis(m: String, ...) => None 22 | fun method_with_default_params(a: U8 = 0, b: U8 = 1) => None 23 | 24 | fun apply() => 25 | @printf("foo".cstring() where okay = false) 26 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_jump.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR A return statement can only appear in a method body. 4 | let field_default_return: None = (return None) 5 | ^~~~~~ 6 | $ERROR A return statement can only appear in a method body. 7 | fun parameter_default_return(x: None = (return None)) => None 8 | ^~~~~~ 9 | $ERROR A return statement in a constructor cannot have a value expression. 10 | new constructor_return_this() => return this 11 | ^~~~ 12 | $ERROR A return statement in a behaviour cannot have a value expression. 13 | be behaviour_return_this() => return this 14 | ^~~~ 15 | $ERROR A compile intrinsic must be the entire method body. 16 | fun true_intrinsic() => true; compile_intrinsic 17 | ^~~~~~~~~~~~~~~~~ 18 | $ERROR A compile intrinsic must be the entire method body. 19 | fun if_intrinsic() => if true then compile_intrinsic end 20 | ^~~~~~~~~~~~~~~~~ 21 | $ERROR A compile intrinsic cannot have a value expression. 22 | fun intrinsic_true() => compile_intrinsic true 23 | ^~~~ 24 | $ERROR A compile error must have a string value explaining the error. 25 | ifdef windows then compile_error end 26 | ^~~~~~~~~~~~~ 27 | $ERROR A compile error must have a string value explaining the error. 28 | ifdef windows then compile_error true end 29 | ^~~~ 30 | $ERROR A compile error must be in an ifdef clause. 31 | compile_error "sorry" 32 | ^~~~~~~~~~~~~ 33 | $ERROR A compile error must be the entire ifdef clause body. 34 | ifdef windows then true; compile_error "sorry" end 35 | ^~~~~~~~~~~~~ 36 | $ERROR An error statement cannot have a value expression. 37 | error "value" 38 | ^~~~~~~ 39 | """ 40 | 41 | actor A 42 | let field_default_return: None = (return None) 43 | 44 | fun parameter_default_return(x: None = (return None)) => None 45 | 46 | new constructor_return() => return 47 | new constructor_return_this() => return this 48 | 49 | be behaviour_return() => return 50 | be behaviour_return_this() => return this 51 | 52 | fun intrinsic() => compile_intrinsic 53 | fun true_intrinsic() => true; compile_intrinsic 54 | fun if_intrinsic() => if true then compile_intrinsic end 55 | fun intrinsic_true() => compile_intrinsic true 56 | 57 | fun apply() => 58 | ifdef windows then compile_error end 59 | ifdef windows then compile_error "sorry" end 60 | ifdef windows then compile_error true end 61 | compile_error "sorry" 62 | ifdef windows then true; compile_error "sorry" end 63 | 64 | error 65 | error "value" 66 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_lambda.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR A lambda capture cannot specify a type without a value. 4 | let lambda_capture_type_no_value = {()(a: Bool) => None } 5 | ^~~~ 6 | $ERROR A bare lambda cannot specify a receiver capability. 7 | let bare_lambda_ref_receiver = @{ref(a: I32): I32 => a + 1 } 8 | ^~~ 9 | $ERROR A bare lambda cannot have type parameters. 10 | let bare_lambda_type_params = @{[A: Any](a: I32): I32 => a + 1 } 11 | ^ 12 | $ERROR A bare lambda cannot have captures. 13 | let bare_lambda_captures = @{(a: I32)(b = true): I32 => a + 1 } 14 | ^ 15 | $ERROR A bare lambda can only have the `val` capability. 16 | let bare_lambda_ref_object = @{(a: I32): I32 => a + 1 } ref 17 | ^~~ 18 | $ERROR A bare lambda type cannot specify a receiver capability. 19 | let bare_lambda_type_ref_receiver: (@{ref(I32): I32} | None) = None 20 | ^~~ 21 | $ERROR A bare lambda type cannot have type parameters. 22 | let bare_lambda_type_type_params: (@{[A: Any](I32): I32} | None) = None 23 | ^ 24 | $ERROR A bare lambda type can only have the `val` capability. 25 | let bare_lambda_type_ref_object: (@{(I32): I32} ref | None) = None 26 | ^~~ 27 | """ 28 | 29 | actor A 30 | fun apply() => 31 | let lambda_capture_value = {()(a = true) => None } 32 | let lambda_capture_type_value = {()(a: Bool = true) => None } 33 | let lambda_capture_type_no_value = {()(a: Bool) => None } 34 | 35 | let bare_lambda_ref_receiver = @{ref(a: I32): I32 => a + 1 } 36 | let bare_lambda_type_params = @{[A: Any](a: I32): I32 => a + 1 } 37 | let bare_lambda_captures = @{(a: I32)(b = true): I32 => a + 1 } 38 | let bare_lambda_ref_object = @{(a: I32): I32 => a + 1 } ref 39 | let bare_lambda_val_object = @{(a: I32): I32 => a + 1 } val 40 | 41 | let bare_lambda_type: (@{(I32): I32} | None) = None 42 | let bare_lambda_type_ref_receiver: (@{ref(I32): I32} | None) = None 43 | let bare_lambda_type_type_params: (@{[A: Any](I32): I32} | None) = None 44 | let bare_lambda_type_ref_object: (@{(I32): I32} ref | None) = None 45 | let bare_lambda_type_val_object: (@{(I32): I32} val | None) = None 46 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_match.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR The final case in a match block must have a body. 4 | match u | 0 | 1 else None end 5 | ^ 6 | $ERROR A match capture cannot be declared as `var`; use `let` instead. 7 | match u | var x: X => true end 8 | ^~~ 9 | $ERROR The type of a match capture must be explicitly declared. 10 | match u | let x => true end 11 | ^~~ 12 | $ERROR A match cannot capture a tuple; use a tuple of match captures instead. 13 | match u | let x: (A, B) => true end 14 | ^ 15 | $ERROR A match capture cannot be declared as `var`; use `let` instead. 16 | match u | (var a: A, let b) => true end 17 | ^~~ 18 | $ERROR The type of a match capture must be explicitly declared. 19 | match u | (var a: A, let b) => true end 20 | ^~~ 21 | $ERROR A sequence inside a match capture must have only one element. 22 | match u | (false; let x: X) => true end 23 | ^~~~~ 24 | $ERROR A sequence inside a match capture must have only one element. 25 | match u | (false; false) => true end 26 | ^~~~~ 27 | $ERROR A sequence inside a match capture must have only one element. 28 | match u | (let a: A, false; let b) => true end 29 | ^~~~~ 30 | $ERROR A sequence inside a match capture must have only one element. 31 | match u | (true, false; false) => true end 32 | ^~~~~ 33 | """ 34 | 35 | primitive P 36 | fun apply(u: U64) => 37 | match u | 0 | 1 => None else None end 38 | match u | 0 | 1 else None end 39 | 40 | match u | let x: X => true end 41 | match u | var x: X => true end 42 | match u | let x => true end 43 | 44 | match u | let x: (A, B) => true end 45 | match u | (let a: A, let b: B) => true end 46 | match u | (var a: A, let b) => true end 47 | 48 | match u | (let x: X) => true end 49 | match u | (false; let x: X) => true end 50 | match u | (false; false) => true end 51 | match u | (let a: A, false; let b) => true end 52 | match u | (true, false; false) => true end 53 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_method.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR A behaviour cannot specify a receiver capability. 4 | be val interface_behaviour_with_cap() 5 | ^~~ 6 | $ERROR A behaviour cannot specify a receiver capability. 7 | be val trait_behaviour_with_cap() 8 | ^~~ 9 | $ERROR A primitive constructor cannot specify a receiver capability. 10 | new iso primitive_constructor_with_cap() => None 11 | ^~~ 12 | $ERROR A behaviour cannot specify a receiver capability. 13 | be val actor_behaviour_with_cap() => None 14 | ^~~ 15 | $ERROR Only functions can be bare. 16 | new @actor_constructor_bare() => None 17 | ^ 18 | $ERROR Only functions can be bare. 19 | be @actor_behaviour_bare() => None 20 | ^ 21 | $ERROR Only functions can specify a return type. 22 | new interface_constructor_with_return_type(): X 23 | ^ 24 | $ERROR Only functions can specify a return type. 25 | be interface_behaviour_with_return_type(): X 26 | ^ 27 | $ERROR Only functions can specify a return type. 28 | new trait_constructor_with_return_type(): X 29 | ^ 30 | $ERROR Only functions can specify a return type. 31 | be trait_behaviour_with_return_type(): X 32 | ^ 33 | $ERROR Only functions can specify a return type. 34 | new primitive_constructor_with_return_type(): X => None 35 | ^ 36 | $ERROR Only functions can specify a return type. 37 | new struct_constructor_with_return_type(): X => None 38 | ^ 39 | $ERROR Only functions can specify a return type. 40 | new class_constructor_with_return_type(): X => None 41 | ^ 42 | $ERROR Only functions can specify a return type. 43 | new actor_constructor_with_return_type(): X => None 44 | ^ 45 | $ERROR Only functions can specify a return type. 46 | be actor_behaviour_with_return_type(): X => None 47 | ^ 48 | $ERROR A behaviour cannot be partial. 49 | be interface_behaviour_partial() ? 50 | ^ 51 | $ERROR A behaviour cannot be partial. 52 | be trait_behaviour_partial() ? 53 | ^ 54 | $ERROR An actor constructor cannot be partial. 55 | new actor_constructor_partial() ? => None 56 | ^ 57 | $ERROR A behaviour cannot be partial. 58 | be actor_behaviour_partial() ? => None 59 | ^ 60 | $ERROR A trait or interface constructor cannot provide a body. 61 | new interface_constructor_with_body() => None 62 | ^~~~ 63 | $ERROR A trait or interface constructor cannot provide a body. 64 | new trait_constructor_with_body() => None 65 | ^~~~ 66 | $ERROR This function must provide a body. 67 | fun primitive_function_no_body() 68 | ^~~ 69 | $ERROR This constructor must provide a body. 70 | new primitive_constructor_no_body() 71 | ^~~ 72 | $ERROR This function must provide a body. 73 | fun struct_function_no_body() 74 | ^~~ 75 | $ERROR This constructor must provide a body. 76 | new struct_constructor_no_body() 77 | ^~~ 78 | $ERROR This function must provide a body. 79 | fun class_function_no_body() 80 | ^~~ 81 | $ERROR This constructor must provide a body. 82 | new class_constructor_no_body() 83 | ^~~ 84 | $ERROR This function must provide a body. 85 | fun actor_function_no_body() 86 | ^~~ 87 | $ERROR This constructor must provide a body. 88 | new actor_constructor_no_body() 89 | ^~~ 90 | $ERROR This behaviour must provide a body. 91 | be actor_behaviour_no_body() 92 | ^~ 93 | $ERROR Method guards are not yet supported in this compiler. 94 | fun method_with_guard() if false => None 95 | ^~~~~ 96 | """ 97 | 98 | interface InterfaceMethodsWithCap 99 | fun box interface_function_with_cap() 100 | new iso interface_constructor_with_cap() 101 | be val interface_behaviour_with_cap() 102 | 103 | trait TraitMethodsWithCap 104 | fun box trait_function_with_cap() 105 | new iso trait_constructor_with_cap() 106 | be val trait_behaviour_with_cap() 107 | 108 | primitive PrimitiveMethodsWithCap 109 | fun box primitive_function_with_cap() => None 110 | new iso primitive_constructor_with_cap() => None 111 | 112 | struct StructMethodsWithCap 113 | fun box struct_function_with_cap() => None 114 | new iso struct_constructor_with_cap() => None 115 | 116 | class ClassMethodsWithCap 117 | fun box class_function_with_cap() => None 118 | new iso class_constructor_with_cap() => None 119 | 120 | actor ActorMethodsWithCap 121 | fun box actor_function_with_cap() => None 122 | new iso actor_constructor_with_cap() => None 123 | be val actor_behaviour_with_cap() => None 124 | 125 | actor ActorMethodsBare 126 | fun @actor_function_bare() => None 127 | new @actor_constructor_bare() => None 128 | be @actor_behaviour_bare() => None 129 | 130 | interface InterfaceMethodsWithReturnType 131 | fun interface_function_with_return_type(): X 132 | new interface_constructor_with_return_type(): X 133 | be interface_behaviour_with_return_type(): X 134 | 135 | trait TraitMethodsWithReturnType 136 | fun trait_function_with_return_type(): X 137 | new trait_constructor_with_return_type(): X 138 | be trait_behaviour_with_return_type(): X 139 | 140 | primitive PrimitiveMethodsWithReturnType 141 | fun primitive_function_with_return_type(): X => None 142 | new primitive_constructor_with_return_type(): X => None 143 | 144 | struct StructMethodsWithReturnType 145 | fun struct_function_with_return_type(): X => None 146 | new struct_constructor_with_return_type(): X => None 147 | 148 | class ClassMethodsWithReturnType 149 | fun class_function_with_return_type(): X => None 150 | new class_constructor_with_return_type(): X => None 151 | 152 | actor ActorMethodsWithReturnType 153 | fun actor_function_with_return_type(): X => None 154 | new actor_constructor_with_return_type(): X => None 155 | be actor_behaviour_with_return_type(): X => None 156 | 157 | interface InterfaceMethodsPartial 158 | fun interface_function_partial() ? 159 | new interface_constructor_partial() ? 160 | be interface_behaviour_partial() ? 161 | 162 | trait TraitMethodsPartial 163 | fun trait_function_partial() ? 164 | new trait_constructor_partial() ? 165 | be trait_behaviour_partial() ? 166 | 167 | primitive PrimitiveMethodsPartial 168 | fun primitive_function_partial() ? => None 169 | new primitive_constructor_partial() ? => None 170 | 171 | struct StructMethodsPartial 172 | fun struct_function_partial() ? => None 173 | new struct_constructor_partial() ? => None 174 | 175 | class ClassMethodsPartial 176 | fun class_function_partial() ? => None 177 | new class_constructor_partial() ? => None 178 | 179 | actor ActorMethodsPartial 180 | fun actor_function_partial() ? => None 181 | new actor_constructor_partial() ? => None 182 | be actor_behaviour_partial() ? => None 183 | 184 | interface InterfaceMethodsWithBody 185 | fun interface_function_with_body() => None 186 | new interface_constructor_with_body() => None 187 | be interface_behaviour_with_body() => None 188 | 189 | trait TraitMethodsWithBody 190 | fun trait_function_with_body() => None 191 | new trait_constructor_with_body() => None 192 | be trait_behaviour_with_body() => None 193 | 194 | primitive PrimitiveMethodsWithBody 195 | fun primitive_function_with_body() => None 196 | new primitive_constructor_with_body() => None 197 | 198 | struct StructMethodsWithBody 199 | fun struct_function_with_body() => None 200 | new struct_constructor_with_body() => None 201 | 202 | class ClassMethodsWithBody 203 | fun class_function_with_body() => None 204 | new class_constructor_with_body() => None 205 | 206 | actor ActorMethodsWithBody 207 | fun actor_function_with_body() => None 208 | new actor_constructor_with_body() => None 209 | be actor_behaviour_with_body() => None 210 | 211 | interface InterfaceMethodsNoBody 212 | fun interface_function_no_body() 213 | new interface_constructor_no_body() 214 | be interface_behaviour_no_body() 215 | 216 | trait TraitMethodsNoBody 217 | fun trait_function_no_body() 218 | new trait_constructor_no_body() 219 | be trait_behaviour_no_body() 220 | 221 | primitive PrimitiveMethodsNoBody 222 | fun primitive_function_no_body() 223 | new primitive_constructor_no_body() 224 | 225 | struct StructMethodsNoBody 226 | fun struct_function_no_body() 227 | new struct_constructor_no_body() 228 | 229 | class ClassMethodsNoBody 230 | fun class_function_no_body() 231 | new class_constructor_no_body() 232 | 233 | actor ActorMethodsNoBody 234 | fun actor_function_no_body() 235 | new actor_constructor_no_body() 236 | be actor_behaviour_no_body() 237 | 238 | primitive P 239 | fun method_with_guard() if false => None 240 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_subdir.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR A type alias must specify a type. 4 | type SubdirTypeAliasWithoutType 5 | ^~~~ 6 | """ 7 | 8 | use "syntax_subdir" 9 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_subdir/main.pony: -------------------------------------------------------------------------------- 1 | type SubdirTypeAliasWithoutType 2 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_type.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR `this` can only be used in a type as a viewpoint. 4 | fun this_type(): this => None 5 | ^~~~ 6 | $ERROR `this` cannot appear on the right side of a viewpoint type. 7 | fun type_arrow_this(): None->this => None 8 | ^~~~ 9 | $ERROR A tuple cannot be used as a type parameter constraint. 10 | fun constraint_tuple[A: (U8, U8)]() => None 11 | ^ 12 | $ERROR A tuple cannot be used as a type parameter constraint. 13 | fun constraint_union_tuple[A: ((U8, U8) | U64)]() => None 14 | ^ 15 | $ERROR A type cannot be only a reference capability. 16 | let v: val = true 17 | ^~~ 18 | $ERROR A reference capability set can only appear in a constraint. 19 | let v: #share = true 20 | ^~~~~~ 21 | $ERROR A type cannot be only a reference capability. 22 | let v: #share = true 23 | ^~~~~~ 24 | $ERROR A reference capability set can only appear in a constraint. 25 | let v: Any #share = true 26 | ^~~~~~ 27 | """ 28 | 29 | primitive P 30 | fun this_type(): this => None 31 | fun this_arrow_type(): this->None => None 32 | fun type_arrow_this(): None->this => None 33 | 34 | fun constraint_tuple[A: (U8, U8)]() => None 35 | fun constraint_union_tuple[A: ((U8, U8) | U64)]() => None 36 | fun constraint_type_arg_tuple[A: Array[(U8, U8)]]() => None 37 | 38 | fun apply() => 39 | let v: val = true 40 | let v: #share = true 41 | let v: Any #share = true 42 | -------------------------------------------------------------------------------- /ponycc/test/fixtures/syntax_type_decl.pony: -------------------------------------------------------------------------------- 1 | """ 2 | $SYNTAX 3 | $ERROR A type alias must specify a type. 4 | type TypeAliasWithoutType 5 | ^~~~ 6 | $ERROR A type alias cannot specify a default capability. 7 | type box TypeAliasWithCap is X 8 | ^~~ 9 | $ERROR A primitive cannot specify a default capability. 10 | primitive box PrimitiveWithCap 11 | ^~~ 12 | $ERROR An actor cannot specify a default capability. 13 | actor box ActorWithCap 14 | ^~~ 15 | $ERROR A type alias cannot specify a C API. 16 | type @TypeAliasWithCAPI is X 17 | ^ 18 | $ERROR An interface cannot specify a C API. 19 | interface @InterfaceWithCAPI 20 | ^ 21 | $ERROR A trait cannot specify a C API. 22 | trait @TraitWithCAPI 23 | ^ 24 | $ERROR A primitive cannot specify a C API. 25 | primitive @PrimitiveWithCAPI 26 | ^ 27 | $ERROR A struct cannot specify a C API. 28 | struct @StructWithCAPI 29 | ^ 30 | $ERROR A class cannot specify a C API. 31 | class @ClassWithCAPI 32 | ^ 33 | $ERROR A C API type cannot have type parameters. 34 | actor @CAPIWithTypeParameters[X] 35 | ^ 36 | $ERROR A type alias cannot be named Main - Main must be an actor. 37 | type Main is X 38 | ^~~~ 39 | $ERROR An interface cannot be named Main - Main must be an actor. 40 | interface Main 41 | ^~~~ 42 | $ERROR A trait cannot be named Main - Main must be an actor. 43 | trait Main 44 | ^~~~ 45 | $ERROR A primitive cannot be named Main - Main must be an actor. 46 | primitive Main 47 | ^~~~ 48 | $ERROR A struct cannot be named Main - Main must be an actor. 49 | struct Main 50 | ^~~~ 51 | $ERROR A class cannot be named Main - Main must be an actor. 52 | class Main 53 | ^~~~ 54 | $ERROR A type alias cannot have fields. 55 | let type_field: X 56 | ^~~ 57 | $ERROR An interface cannot have fields. 58 | let interface_field: X 59 | ^~~ 60 | $ERROR A trait cannot have fields. 61 | let trait_field: X 62 | ^~~ 63 | $ERROR A primitive cannot have fields. 64 | let primitive_field: X 65 | ^~~ 66 | $ERROR A type alias cannot have methods. 67 | fun type_method() => None 68 | ^~~ 69 | $ERROR A type alias cannot have methods. 70 | be type_behaviour() => None 71 | ^~ 72 | $ERROR A primitive cannot have behaviours. 73 | be primitive_behaviour() => None 74 | ^~ 75 | $ERROR A struct cannot have behaviours. 76 | be struct_behaviour() => None 77 | ^~ 78 | $ERROR A class cannot have behaviours. 79 | be class_behaviour() => None 80 | ^~ 81 | """ 82 | 83 | type TypeAliasWithoutType 84 | 85 | type box TypeAliasWithCap is X 86 | interface box InterfaceWithCap 87 | trait box TraitWithCap 88 | primitive box PrimitiveWithCap 89 | struct box StructWithCap 90 | class box ClassWithCap 91 | actor box ActorWithCap 92 | 93 | type @TypeAliasWithCAPI is X 94 | interface @InterfaceWithCAPI 95 | trait @TraitWithCAPI 96 | primitive @PrimitiveWithCAPI 97 | struct @StructWithCAPI 98 | class @ClassWithCAPI 99 | actor @ActorWithCAPI 100 | 101 | actor @CAPIWithTypeParameters[X] 102 | 103 | type Main is X 104 | interface Main 105 | trait Main 106 | primitive Main 107 | struct Main 108 | class Main 109 | actor Main 110 | 111 | type TypeAliasWithField is X 112 | let type_field: X 113 | 114 | interface InterfaceWithField 115 | let interface_field: X 116 | 117 | trait TraitWithField 118 | let trait_field: X 119 | 120 | primitive PrimitiveWithField 121 | let primitive_field: X 122 | 123 | struct StructWithField 124 | let struct_field: X 125 | 126 | class ClassWithField 127 | let class_field: X 128 | 129 | actor ActorWithField 130 | let actor_field: X 131 | 132 | type TypeAliasWithMethod is X 133 | fun type_method() => None 134 | 135 | interface InterfaceWithMethod 136 | fun interface_method() => None 137 | 138 | trait TraitWithMethod 139 | fun trait_method() => None 140 | 141 | primitive PrimitiveWithMethod 142 | fun primitive_method() => None 143 | 144 | struct StructWithMethod 145 | fun struct_method() => None 146 | 147 | class ClassWithMethod 148 | fun class_method() => None 149 | 150 | actor ActorWithMethod 151 | fun actor_method() => None 152 | 153 | type TypeAliasWithBehaviour is X 154 | be type_behaviour() => None 155 | 156 | interface InterfaceWithBehaviour 157 | be interface_behaviour() => None 158 | 159 | trait TraitWithBehaviour 160 | be trait_behaviour() => None 161 | 162 | primitive PrimitiveWithBehaviour 163 | be primitive_behaviour() => None 164 | 165 | struct StructWithBehaviour 166 | be struct_behaviour() => None 167 | 168 | class ClassWithBehaviour 169 | be class_behaviour() => None 170 | 171 | actor ActorWithBehaviour 172 | be actor_behaviour() => None 173 | -------------------------------------------------------------------------------- /ponycc/test/main.pony: -------------------------------------------------------------------------------- 1 | 2 | use "files" 3 | use "glob" 4 | use "ponytest" 5 | 6 | use "../ast" 7 | use "../frame" 8 | 9 | actor Main is TestList 10 | new create(env: Env) => 11 | let test = PonyTest(env, this) 12 | 13 | try 14 | let auth = env.root as AmbientAuth 15 | let test_root = FilePath(auth, Path.dir(__loc.file()))? 16 | let fixtures = Glob.glob(test_root, "fixtures/*.pony") 17 | 18 | for path in fixtures.values() do 19 | let file = OpenFile(path) as File 20 | let source = Source(file.read_string(file.size()), path.path) 21 | 22 | test(TestFixture(source)) 23 | end 24 | else 25 | env.out.print("An error occurred opening the test fixture files.") 26 | end 27 | 28 | fun tag tests(test: PonyTest) => 29 | // All test cases have already been dynamically applied in the constructor, 30 | // so there is no need to apply anything here. 31 | None 32 | -------------------------------------------------------------------------------- /ponycc/test/test_command.pony: -------------------------------------------------------------------------------- 1 | 2 | use "ponytest" 3 | 4 | use "../ast" 5 | use "../pass" 6 | 7 | interface val TestCommandAny 8 | fun h(): TestHelper 9 | fun auth(): AmbientAuth 10 | 11 | fun ref add_line(l: String) 12 | fun ref add_error(e: TestCommand[_Error]) 13 | fun ref add_check(c: TestCommand[_Check]) 14 | 15 | fun val apply(source: Source) 16 | 17 | fun print_errors(actual_errors: Array[PassError] box) 18 | 19 | fun check_errors(actual_errors: Array[PassError] box) 20 | 21 | fun check_checks(program: Program) 22 | 23 | class val TestCommand[T: TestCommandType val] 24 | let _h: TestHelper 25 | let _auth: AmbientAuth 26 | 27 | let message: String 28 | embed lines: Array[String] = [] 29 | embed errors: Array[TestCommand[_Error]] = [] 30 | embed checks: Array[TestCommand[_Check]] = [] 31 | 32 | new iso create(h': TestHelper, m': String)? => 33 | (_h, message, _auth) = (h', m', h'.env.root as AmbientAuth) 34 | 35 | fun h(): TestHelper => _h 36 | fun auth(): AmbientAuth => _auth 37 | 38 | fun ref add_line(l: String) => lines.push(l) 39 | fun ref add_error(e: TestCommand[_Error]) => errors.push(e); e 40 | fun ref add_check(c: TestCommand[_Check]) => checks.push(c); c 41 | 42 | fun val apply(source: Source) => 43 | _h.long_test(5_000_000_000) // 5 second timeout 44 | T(this, source) 45 | 46 | fun print_errors(actual_errors: Array[PassError] box) => 47 | for err in actual_errors.values() do 48 | _h.log(err.message) 49 | (let pos1, let pos2) = err.pos.show_in_line() 50 | _h.log(pos1) 51 | _h.log(pos2) 52 | end 53 | 54 | fun check_errors(actual_errors: Array[PassError] box) => 55 | _h.assert_eq[Bool](actual_errors.size() == 0, errors.size() == 0, "Success") 56 | 57 | for (i, expect) in errors.pairs() do 58 | try 59 | let actual = actual_errors(i)? 60 | try 61 | actual.message.find(expect.message)? 62 | else 63 | _h.fail("error did not match expected message") 64 | _h.fail("expected: " + expect.message) 65 | _h.fail("actual: " + actual.message) 66 | end 67 | (let line_1, let line_2) = actual.pos.show_in_line() 68 | try _h.assert_eq[String](expect.lines(0)?, line_1) end 69 | try _h.assert_eq[String](expect.lines(1)?, line_2) end 70 | else 71 | _h.fail("expected error at index " + i.string() + " is missing") 72 | end 73 | end 74 | 75 | if not 76 | _h.assert_eq[USize]( 77 | errors.size(), actual_errors.size(), "Number of Errors") 78 | then 79 | print_errors(actual_errors) 80 | end 81 | 82 | fun check_checks(program: Program) => 83 | for check in checks.values() do 84 | program.get_child_dynamic_path(check.message.clone().>strip(), {(ast) => 85 | try 86 | _h.assert_eq[String]( 87 | String.join(check.lines.values()), 88 | (ast as AST).string()) 89 | else 90 | _h.fail("Check failed to find dynamic path: " + check.message) 91 | end 92 | }) 93 | end 94 | -------------------------------------------------------------------------------- /ponycc/test/test_command_type.pony: -------------------------------------------------------------------------------- 1 | 2 | use ".." 3 | use "../ast" 4 | use "../frame" 5 | use "../pass/print" 6 | use "../pass/parse" 7 | use "../pass/syntax" 8 | use "../pass/sugar" 9 | use "../pass/names" 10 | 11 | trait val TestCommandType 12 | new val create() 13 | fun apply(command: TestCommandAny, source: Source) => None 14 | 15 | primitive _Error is TestCommandType 16 | 17 | primitive _Check is TestCommandType 18 | 19 | primitive _Print is TestCommandType 20 | fun apply(command: TestCommandAny, source: Source) => 21 | BuildCompiler[Source, Module](Parse) 22 | .next[String](Print) 23 | .on_errors({(pass, errs) => 24 | command.print_errors(errs) 25 | command.h().fail("Unexpected " + pass.name() + " error(s) occurred.") 26 | }) 27 | .on_complete({(string) => 28 | command.h().assert_eq[String](source.content(), string) 29 | command.h().complete(true) 30 | }) 31 | .apply(source) 32 | 33 | primitive _Parse is TestCommandType 34 | fun apply(command: TestCommandAny, source: Source) => 35 | BuildCompiler[Source, Module](Parse) 36 | .on_errors({(pass, errs) => 37 | command.check_errors(errs) 38 | command.h().complete(true) 39 | }) 40 | .on_complete({(module) => 41 | command.check_errors([]) 42 | command.h().complete(true) 43 | }) 44 | .apply(source) 45 | 46 | primitive _Syntax is TestCommandType 47 | fun apply(command: TestCommandAny, source: Source) => 48 | let resolve_sources = ResolveSourceFiles(command.auth()) 49 | BuildCompiler[Sources, Program](ParseProgramFiles(resolve_sources)) 50 | .next[Program](Syntax) 51 | .on_errors({(pass, errs) => 52 | match pass | Syntax => 53 | command.check_errors(errs) 54 | command.h().complete(true) 55 | else 56 | command.print_errors(errs) 57 | command.h().fail("Unexpected " + pass.name() + " error(s) occurred.") 58 | end 59 | }) 60 | .on_complete({(module) => 61 | command.check_checks(module) 62 | command.h().complete(true) 63 | }) 64 | .apply([source]) 65 | 66 | primitive _Sugar is TestCommandType 67 | fun apply(command: TestCommandAny, source: Source) => 68 | let resolve_sources = ResolveSourceFiles(command.auth()) 69 | BuildCompiler[Sources, Program](ParseProgramFiles(resolve_sources)) 70 | .next[Program](Syntax) 71 | .next[Program](Sugar) 72 | .on_errors({(pass, errs) => 73 | match pass | Sugar => 74 | command.check_errors(errs) 75 | command.h().complete(true) 76 | else 77 | command.print_errors(errs) 78 | command.h().fail("Unexpected " + pass.name() + " error(s) occurred.") 79 | end 80 | }) 81 | .on_complete({(module) => 82 | command.check_checks(module) 83 | command.h().complete(true) 84 | }) 85 | .apply([source]) 86 | 87 | primitive _Names is TestCommandType 88 | fun apply(command: TestCommandAny, source: Source) => 89 | let resolve_sources = ResolveSourceFiles(command.auth()) 90 | BuildCompiler[Sources, Program](ParseProgramFiles(resolve_sources)) 91 | .next[Program](Syntax) 92 | .next[Program](Sugar) 93 | .next[Program](Names) 94 | .on_errors({(pass, errs) => 95 | match pass | Names => 96 | command.check_errors(errs) 97 | command.h().complete(true) 98 | else 99 | command.print_errors(errs) 100 | command.h().fail("Unexpected " + pass.name() + " error(s) occurred.") 101 | end 102 | }) 103 | .on_complete({(module) => 104 | command.check_errors([]) 105 | command.check_checks(module) 106 | command.h().complete(true) 107 | }) 108 | .apply([source]) 109 | -------------------------------------------------------------------------------- /ponycc/test/test_fixture.pony: -------------------------------------------------------------------------------- 1 | 2 | use "collections" 3 | use "files" 4 | use "ponytest" 5 | 6 | use "../ast" 7 | 8 | class TestFixture is UnitTest 9 | let source: Source 10 | 11 | new iso create(source': Source) => source = source' 12 | 13 | fun name(): String => 14 | try Path.rel(Path.dir(__loc.file()), source.path())? 15 | else source.path() 16 | end 17 | 18 | fun apply(h: TestHelper) => 19 | let env = h.env 20 | 21 | // Confirm that the start of the source file is a docstring. 22 | let triple = "\"\"\"" 23 | var i: ISize = 0 24 | if (try source.content().find(triple)? == 0 else false end) then 25 | i = triple.size().isize() 26 | else 27 | return h.fail("Expected the start of the source file to be a docstring.") 28 | end 29 | 30 | // Parse the list of commands from the source file docstring. 31 | let commands = List[TestCommandAny iso] 32 | try 33 | while true do 34 | i = i + 1 35 | let j = source.content().find("\n", i)? 36 | let line: String = source.content().substring(i, j) 37 | i = j 38 | 39 | commands.unshift( 40 | if line.at(triple) then break 41 | elseif line.at("$PRINT") then TestCommand[_Print](h, line.substring(7))? 42 | elseif line.at("$PARSE") then TestCommand[_Parse](h, line.substring(7))? 43 | elseif line.at("$SYNTAX") then TestCommand[_Syntax](h, line.substring(8))? 44 | elseif line.at("$SUGAR") then TestCommand[_Sugar](h, line.substring(7))? 45 | elseif line.at("$NAMES") then TestCommand[_Names](h, line.substring(7))? 46 | elseif line.at("$ERROR") then TestCommand[_Error](h, line.substring(7))? 47 | elseif line.at("$CHECK") then TestCommand[_Check](h, line.substring(7))? 48 | else commands.shift()? .> add_line(line) 49 | end 50 | ) 51 | end 52 | else 53 | return h.fail("Expected to be able to parse the test plan docstring.") 54 | end 55 | 56 | // Associate ERROR and CHECK commands with their preceding parent commands. 57 | try 58 | let errors = List[TestCommand[_Error]] 59 | let checks = List[TestCommand[_Check]] 60 | 61 | for _ in Range(0, commands.size()) do 62 | match commands.shift()? 63 | | let e: TestCommand[_Error] iso => errors.unshift(consume e) 64 | | let c: TestCommand[_Check] iso => checks.unshift(consume c) 65 | | let x: TestCommandAny iso => 66 | try while true do x.add_error(errors.shift()?) end end 67 | try while true do x.add_check(checks.shift()?) end end 68 | commands.push(consume x) 69 | end 70 | end 71 | end 72 | 73 | // Execute the final list of commands. 74 | try while true do commands.shift()?(source) end end 75 | -------------------------------------------------------------------------------- /ponycc/unreachable/unreachable.pony: -------------------------------------------------------------------------------- 1 | 2 | class Unreachable 3 | new create(value: (Stringable | None) = None, loc: SourceLoc = __loc) => 4 | @fprintf[I32](@pony_os_stderr[Pointer[U8]](), 5 | "ABORT: Unreachable condition at %s:%zu (in %s method)\n".cstring(), 6 | loc.file().cstring(), loc.line(), loc.method_name().cstring()) 7 | 8 | if value isnt None then 9 | @printf[I32]("%s\n".cstring(), value.string().cstring()) 10 | end 11 | 12 | @exit[None](I32(1)) 13 | --------------------------------------------------------------------------------