├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── README.md ├── bootstrap └── stage0.c ├── compiler ├── README.md ├── ast.ae ├── codegen.ae ├── errors.ae ├── lexer.ae ├── main.ae ├── parser.ae ├── tokens.ae ├── typecheck.ae ├── types.ae └── utils.ae ├── examples ├── daecor │ ├── bezier.ae │ ├── rope.ae │ ├── snake.ae │ └── tictactoe.ae ├── glut_orbit.ae ├── linked_list.ae ├── raytrace.ae ├── sdl_raytrace.ae └── torrent.ae ├── find.ae ├── lib ├── bencode.ae ├── buffer.ae ├── bufferio.ae ├── daecor │ ├── all.ae │ ├── assets │ │ ├── FFL.txt │ │ ├── font.ttf │ │ └── pew.wav │ ├── colors.ae │ ├── events.ae │ ├── graphics.ae │ ├── image.ae │ ├── input.ae │ ├── sdl.ae │ ├── sound.ae │ ├── text.ae │ ├── types.ae │ └── vec.ae ├── glut.ae ├── hash │ └── sha1.ae ├── image.ae ├── json.ae ├── map.ae ├── math.ae ├── prelude.ae ├── prelude.h ├── sdl.ae ├── socket.ae ├── span.ae ├── value.ae ├── vec.ae └── vector.ae ├── meta ├── bootstrap.sh ├── gen_bootstrap.sh └── test.py └── tests ├── array_type.ae ├── arrow_function.ae ├── assign.ae ├── bad ├── arith_binop_not_num.ae ├── assign_bad_type.ae ├── assign_method_to_var.ae ├── assign_to_lvalue.ae ├── break_outside.ae ├── call_non_function.ae ├── const_expr_unsupported.ae ├── const_expr_var.ae ├── continue_outside.ae ├── deref_non_ptr.ae ├── enum_call_value.ae ├── enum_get_member_dot.ae ├── enum_method_with_same_name.ae ├── expr-statements │ ├── block_multiple_yields.ae │ ├── if_missing_else.ae │ ├── if_missing_yield.ae │ ├── match_empty.ae │ ├── match_missing_yield.ae │ ├── nested_block_yield.ae │ └── yield_type_mismatch.ae ├── field_type_undef.ae ├── func_bad_arg_type.ae ├── func_bad_num_args.ae ├── func_invalid_param.ae ├── func_invalid_return.ae ├── func_not_found.ae ├── func_redef.ae ├── func_ret_from_void.ae ├── func_ret_type.ae ├── if_not_bool.ae ├── incorrect_labelled_param.ae ├── invalid_yield.ae ├── lexer1.ae ├── logical_ops_not_bool.ae ├── match_duplicate.ae ├── match_not_enum.ae ├── match_not_exhaustive.ae ├── method_ampersand_without_this.ae ├── methods_invalid_struct.ae ├── methods_redef.ae ├── missing_line_end.ae ├── no_return_if.ae ├── no_return_regular.ae ├── no_return_switch.ae ├── no_static_member.ae ├── question_not_ptr.ae ├── sizeof_bad_type.ae ├── static_member_invalid_struct.ae ├── static_method_on_instance.ae ├── struct_method_redef_field.ae ├── struct_redef.ae ├── typed_literals.ae ├── var1.ae ├── var_cannot_infer.ae ├── var_invalid_type.ae ├── var_not_found.ae ├── var_redef.ae ├── var_wrong_type.ae └── while_not_bool.ae ├── binop1.ae ├── binop2.ae ├── binop_order.ae ├── binops.ae ├── break.ae ├── cast.ae ├── chars.ae ├── constants.ae ├── constructors.ae ├── continue.ae ├── defer.ae ├── empty_ret.ae ├── enum.ae ├── enum_auto_dbg.ae ├── escaped_backslash_fstr.ae ├── expr-statements.ae ├── extern.ae ├── file_io.ae ├── float.ae ├── for.ae ├── format_str.ae ├── format_str_specs.ae ├── func_pointers.ae ├── funccall.ae ├── funcorder.ae ├── hashmap_iter.ae ├── higher_order.ae ├── if.ae ├── if_optional_then.ae ├── infer_enums.ae ├── inference.ae ├── instance_method_as_static.ae ├── int_types.ae ├── json_test.ae ├── labelled_params.ae ├── libs.ae ├── logical.ae ├── match.ae ├── match_char.ae ├── match_expr_exit.ae ├── match_no_enum_name.ae ├── match_string.ae ├── method_pass_this_obj_ptr.ae ├── methods.ae ├── multiple_comparison.ae ├── nullptr.ae ├── pointer_to_array.ae ├── pointers.ae ├── print1.ae ├── question.ae ├── relative_imports ├── a.ae ├── b │ ├── b.ae │ └── c │ │ └── c.ae └── main.ae ├── return_analysis.ae ├── return_analysis_exit.ae ├── same_method_name.ae ├── strings.ae ├── structorder.ae ├── structs.ae ├── structs_recursive.ae ├── typecheck_basic.ae ├── typed_int_lits.ae ├── vars.ae └── while.ae /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ae linguist-language=Rust 2 | bootstrap/*.c binary -text -diff 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test_ubuntu: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: 3-stage bootstrap 11 | run: | 12 | cd ${{ github.workspace }} 13 | ./meta/bootstrap.sh 14 | 15 | - name: Running tests with self-hosted compiler 16 | run: | 17 | cd ${{ github.workspace }} 18 | python3 meta/test.py -c bootstrap/aecor 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode 3 | .idea 4 | *.dSYM 5 | .DS_Store 6 | 7 | # We will ignore this by default, add files manually 8 | bootstrap/ 9 | 10 | test.ae 11 | ./*.c 12 | 13 | a.out 14 | out 15 | aecor 16 | find 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aecor 2 | 3 | Statically typed, compiled programming language, transpiled to C. The compiler is self-hosted, 4 | and written in `aecor` itself. You will need `gcc` installed and available in your PATH to be 5 | able to compile `aecor` programs. 6 | 7 | ## Usage 8 | 9 | ### Bootstrapping 10 | 11 | This stage takes the pre-compiled `bootstrap/stage0.c` file and compiles it to make the initial 12 | compiler. It then uses this to re-generate the compiler and verify that it is correct. If successful, 13 | the output is the `./bootstrap/aecor` executable which can be used to compile other programs. 14 | 15 | ```bash 16 | $ ./meta/bootstrap.sh # Generates ./bootstrap/aecor if successful 17 | $ ./bootstrap/aecor -h 18 | ``` 19 | 20 | ### Setting up environment [recommended] 21 | 22 | In order to use `aecor` compiler from a folder that is not repository root, it is recommended to add the following to your `.bashrc` 23 | (or other way of setting up envionment in your shell) 24 | 25 | ```bash 26 | # To find libraries 27 | export AECOR_ROOT="/path/to/aecor/repo/" 28 | # To add compiler to path 29 | export PATH="$PATH:$AECOR_ROOT/bootstrap/" 30 | ``` 31 | 32 | ### Compiling other programs 33 | 34 | For now, the compiler _needs_ to be run from the root of the repository, since it uses relative paths. 35 | By default, the compiler will generate C code and also compile it, but this can be customized: 36 | 37 | ```bash 38 | # generates ./build/out and ./build/out.c 39 | $ aecor /path/to/file.ae 40 | 41 | # generates ./build/out and ./build/temp.c 42 | $ aecor /path/to/file.ae -o ./build/out -c /build/temp.c 43 | 44 | # generates only ./temp.c 45 | $ aecor /path/to/file.ae -n -c ./temp.c 46 | ``` 47 | 48 | ### Running tests 49 | 50 | The `meta/test.py` script can be used to run the tests. It takes in the path to the compiler executable 51 | to be tested, and paths to the files/directories containing the tests. 52 | 53 | _Note: Specify `./` before the executable name for relative paths._ 54 | 55 | ```bash 56 | # Test the bootstrapped compiler on a particular test 57 | $ python3 test.py -c ./bootstrap/aecor /path/to/file.ae 58 | 59 | # Test the bootstrapped compiler on all tests in given directories 60 | $ python3 test.py -c ./bootstrap/aecor /folder/with/tests /another/one 61 | 62 | # Run all the tests (no arguments) 63 | $ python3 test.py -c ./build/aecor 64 | ``` 65 | 66 | ### Development 67 | 68 | If you wish to develop on the compiler, here is my workflow, which may be helpful: 69 | 70 | ```bash 71 | $ ./meta/bootstrap.sh 72 | # > Add feature to compiler source code 73 | $ aecor compiler/main.ae -o build/aecor 74 | # > Add a test for the new feature 75 | $ python3 meta/test.py -c ./build/aecor # make sure it all passes! 76 | ``` 77 | 78 | Essentially, keep `bootstrap/aecor` as the canonical compiler which will not break, and `build/aecor` 79 | as the temporary build for testing. 80 | 81 | Once significant language features are implemented, we should update the bootstrapped files before 82 | we actually use any of these features in the compiler itself, to avoid any circular issues. For instance: if add 83 | feature XYZ to the language and immediately use feature XYZ in the compiler (without updating the bootstrap) it will not work. 84 | 85 | To update the bootstrap, run `./meta/gen_bootstrap.sh`, which performs some sanity checks and then generates the bootstrap. 86 | The script requires all the tests to pass. 87 | -------------------------------------------------------------------------------- /compiler/README.md: -------------------------------------------------------------------------------- 1 | # aecor - Self hosted compiler 2 | 3 | This directory holds the implementation of the self-hosted compiler for `aecor` -------------------------------------------------------------------------------- /compiler/ast.ae: -------------------------------------------------------------------------------- 1 | use "lib/vector.ae" 2 | use "lib/map.ae" 3 | use "compiler/types.ae" 4 | use "compiler/tokens.ae" 5 | use "compiler/errors.ae" 6 | 7 | enum ASTType { 8 | Assignment 9 | Address 10 | And 11 | Block 12 | BoolLiteral 13 | BitwiseAnd 14 | BitwiseNot 15 | BitwiseOr 16 | BitwiseXor 17 | Break 18 | Call 19 | Cast 20 | CharLiteral 21 | Constant 22 | Constructor 23 | Continue 24 | Defer 25 | Dereference 26 | Divide 27 | DivideEquals 28 | EnumValue 29 | Equals 30 | Error 31 | FloatLiteral 32 | FormatStringLiteral 33 | For 34 | GreaterThan 35 | GreaterThanEquals 36 | If 37 | Identifier 38 | Index 39 | IntLiteral 40 | IsNotNull 41 | LeftShift 42 | LessThan 43 | LessThanEquals 44 | Match 45 | Member 46 | Minus 47 | MinusEquals 48 | Modulus 49 | Multiply 50 | MultiplyEquals 51 | Not 52 | NotEquals 53 | Null 54 | Or 55 | Plus 56 | PlusEquals 57 | Return 58 | RightShift 59 | SizeOf 60 | ScopeLookup 61 | StringLiteral 62 | UnaryMinus 63 | VarDeclaration 64 | Yield 65 | While 66 | } 67 | 68 | def ASTType::from_token(type: TokenType): ASTType => match type { 69 | Ampersand => ASTType::BitwiseAnd 70 | And => ASTType::And 71 | Caret => ASTType::BitwiseXor 72 | EqualEquals => ASTType::Equals 73 | Equals => ASTType::Assignment 74 | GreaterThan => ASTType::GreaterThan 75 | GreaterThanEquals => ASTType::GreaterThanEquals 76 | GreaterThanGreaterThan => ASTType::RightShift 77 | LessThan => ASTType::LessThan 78 | LessThanEquals => ASTType::LessThanEquals 79 | LessThanLessThan => ASTType::LeftShift 80 | Line => ASTType::BitwiseOr 81 | Minus => ASTType::Minus 82 | MinusEquals => ASTType::MinusEquals 83 | NotEquals => ASTType::NotEquals 84 | Or => ASTType::Or 85 | Percent => ASTType::Modulus 86 | Plus => ASTType::Plus 87 | PlusEquals => ASTType::PlusEquals 88 | Slash => ASTType::Divide 89 | SlashEquals => ASTType::DivideEquals 90 | Star => ASTType::Multiply 91 | StarEquals => ASTType::MultiplyEquals 92 | Tilde => ASTType::BitwiseNot 93 | else => panic(`Unhandled token type in ASTType::from_token: {type.str()}`) 94 | } 95 | 96 | struct Variable { 97 | name: string 98 | type: &Type 99 | span: Span 100 | 101 | is_extern: bool 102 | extern_name: string 103 | } 104 | 105 | def Variable::new(name: string, type: &Type, span: Span): &Variable { 106 | let var = calloc(1, sizeof(Variable)) as &Variable 107 | var.name = name 108 | var.type = type 109 | var.span = span 110 | return var 111 | } 112 | 113 | struct Function { 114 | name: string 115 | params: &Vector // Vector<&Variable> 116 | return_type: &Type 117 | body: &AST 118 | exits: bool 119 | 120 | type: &Type 121 | span: Span 122 | 123 | is_arrow: bool 124 | 125 | is_extern: bool 126 | extern_name: string 127 | 128 | is_static: bool 129 | is_method: bool 130 | method_struct_name: string 131 | } 132 | 133 | def Function::new(span: Span): &Function { 134 | let func = calloc(1, sizeof(Function)) as &Function 135 | func.params = Vector::new() 136 | func.span = span 137 | return func 138 | } 139 | 140 | struct Structure { 141 | type: &Type 142 | name: string 143 | fields: &Vector // Vector<&Variable> 144 | span: Span 145 | 146 | is_extern: bool 147 | extern_name: string 148 | 149 | is_enum: bool 150 | is_union: bool 151 | } 152 | 153 | def Structure::new(span: Span): &Structure { 154 | let struc = calloc(1, sizeof(Structure)) as &Structure 155 | struc.fields = Vector::new() 156 | struc.span = span 157 | return struc 158 | } 159 | 160 | def Structure::find_field(&this, name: string): &Variable { 161 | for let i = 0; i < .fields.size; i += 1 { 162 | let field = .fields.at(i) as &Variable 163 | if field.name.eq(name) { 164 | return field 165 | } 166 | } 167 | return null 168 | } 169 | 170 | struct Program { 171 | functions: &Vector // Vector<&Function> 172 | structures: &Vector // Vector<&Structure> 173 | constants: &Vector // Vector<&AST> 174 | global_vars: &Vector // Vector<&AST> 175 | span: Span 176 | 177 | included_files: &Map // Map 178 | errors: &Vector // Vector<&Error> 179 | 180 | c_flags: &Vector // Vector 181 | c_includes: &Vector // Vector 182 | c_embed_headers: &Vector // Vector 183 | 184 | methods: &Map // &Map> 185 | } 186 | 187 | def Program::new(): &Program { 188 | let prog = calloc(1, sizeof(Program)) as &Program 189 | prog.functions = Vector::new() 190 | prog.structures = Vector::new() 191 | prog.global_vars = Vector::new() 192 | prog.constants = Vector::new() 193 | 194 | prog.included_files = Map::new() 195 | prog.errors = Vector::new() 196 | 197 | prog.c_flags = Vector::new() 198 | prog.c_includes = Vector::new() 199 | prog.c_embed_headers = Vector::new() 200 | return prog 201 | } 202 | 203 | def Program::is_file_included(&this, filename: string): bool { 204 | let len = filename.len() 205 | if len > 2 and filename[0] == '.' and filename[1] == '/' { 206 | filename = &filename[2] 207 | } 208 | return .included_files.exists(filename) 209 | } 210 | 211 | def Program::add_included_file(&this, filename: string) { 212 | let len = filename.len() 213 | if len > 2 and filename[0] == '.' and filename[1] == '/' { 214 | filename = &filename[2] 215 | } 216 | .included_files.insert(filename, filename) 217 | } 218 | 219 | struct Block { 220 | statements: &Vector // Vector<&AST> 221 | } 222 | 223 | struct Binary { 224 | lhs: &AST 225 | rhs: &AST 226 | } 227 | 228 | struct Identifier { 229 | name: string 230 | var: &Variable 231 | is_function: bool 232 | func: &Function 233 | } 234 | 235 | struct FormatString { 236 | parts: &Vector // Vector 237 | specs: &Vector // Vector 238 | exprs: &Vector // Vector<&AST> 239 | } 240 | 241 | struct Argument { 242 | expr: &AST 243 | label: &AST // Identifier 244 | } 245 | 246 | def Argument::new(label: &AST, expr: &AST): &Argument { 247 | let arg = calloc(1, sizeof(Argument)) as &Argument 248 | arg.expr = expr 249 | arg.label = label 250 | return arg 251 | } 252 | 253 | struct FuncCall { 254 | callee: &AST 255 | args: &Vector // Vector<&Argument> 256 | func: &Function 257 | added_method_arg: bool 258 | } 259 | 260 | struct Constructor { 261 | callee: &AST // Identifier 262 | struc: &Structure 263 | args: &Vector // Vector<&Argument> 264 | } 265 | 266 | struct Member { 267 | lhs: &AST 268 | rhs: &AST // &Identifier 269 | is_method: bool 270 | is_pointer: bool 271 | } 272 | 273 | struct EnumValue { 274 | lhs: &AST 275 | rhs: &AST 276 | struct_def: &Structure 277 | var: &Variable 278 | } 279 | 280 | struct VarDeclaration { 281 | var: &Variable 282 | init: &AST 283 | } 284 | 285 | struct IfStatement { 286 | cond: &AST 287 | body: &AST 288 | els: &AST 289 | } 290 | 291 | struct Loop { 292 | init: &AST 293 | cond: &AST 294 | incr: &AST 295 | body: &AST 296 | } 297 | 298 | struct Cast { 299 | lhs: &AST 300 | to: &Type 301 | } 302 | 303 | struct MatchCase { 304 | cond: &AST 305 | body: &AST 306 | } 307 | 308 | def MatchCase::new(cond: &AST, body: &AST): &MatchCase { 309 | let _case = calloc(1, sizeof(MatchCase)) as &MatchCase 310 | _case.cond = cond 311 | _case.body = body 312 | return _case 313 | } 314 | 315 | struct Match { 316 | expr: &AST 317 | cases: &Vector // Vector<&MatchCase> 318 | defolt: &AST 319 | defolt_span: Span 320 | } 321 | 322 | struct NumLiteral { 323 | text: string 324 | suffix: &Type 325 | } 326 | 327 | union ASTUnion { 328 | block: Block 329 | binary: Binary 330 | ident: Identifier 331 | fmt_str: FormatString 332 | call: FuncCall 333 | member: Member 334 | enum_val: EnumValue 335 | var_decl: VarDeclaration 336 | if_stmt: IfStatement 337 | loop: Loop 338 | cast: Cast 339 | unary: &AST 340 | match_stmt: Match 341 | num_literal: NumLiteral 342 | bool_literal: bool 343 | string_literal: string 344 | char_literal: string 345 | constructor: Constructor 346 | size_of_type: &Type 347 | } 348 | 349 | struct AST { 350 | type: ASTType 351 | span: Span 352 | u: ASTUnion 353 | etype: &Type 354 | returns: bool 355 | } 356 | 357 | def AST::new(type: ASTType, span: Span): &AST { 358 | let ast = calloc(1, sizeof(AST)) as &AST 359 | ast.type = type 360 | ast.span = span 361 | return ast 362 | } 363 | 364 | def AST::new_unop(type: ASTType, span: Span, expr: &AST): &AST { 365 | let ast = AST::new(type, span) 366 | ast.u.unary = expr 367 | return ast 368 | } 369 | 370 | def AST::new_binop(type: ASTType, lhs: &AST, rhs: &AST): &AST { 371 | let ast = AST::new(type, lhs.span.join(rhs.span)) 372 | ast.u.binary.lhs = lhs 373 | ast.u.binary.rhs = rhs 374 | return ast 375 | } 376 | 377 | def AST::callee_is(&this, expected: string): bool { 378 | if not this? return false 379 | if .type != ASTType::Call return false 380 | if .u.call.callee.type != ASTType::Identifier return false 381 | let name = .u.call.callee.u.ident.name 382 | return name.eq(expected) 383 | } 384 | 385 | def AST::is_lvalue(&this): bool => match .type { 386 | Dereference => true 387 | Index => true 388 | Member => not .u.member.is_method 389 | Identifier => not .u.ident.is_function 390 | else => false 391 | } 392 | -------------------------------------------------------------------------------- /compiler/errors.ae: -------------------------------------------------------------------------------- 1 | use "lib/vector.ae" 2 | use "compiler/tokens.ae" 3 | use "compiler/utils.ae" 4 | 5 | enum ErrorType { 6 | Standard 7 | WithNote 8 | WithHint 9 | } 10 | 11 | def ErrorType::str(this): string => match this { 12 | Standard => "Standard" 13 | WithNote => "WithNote" 14 | WithHint => "WithHint" 15 | } 16 | 17 | struct Error { 18 | type: ErrorType 19 | msg1: string 20 | span1: Span 21 | msg2: string 22 | span2: Span 23 | } 24 | 25 | enum MessageType { 26 | Error 27 | Warning 28 | Note 29 | } 30 | 31 | def MessageType::to_color(this): string => match this { 32 | Error => "\x1b[31m" 33 | Warning => "\x1b[33m" 34 | Note => "\x1b[32m" 35 | } 36 | 37 | def MessageType::str(this): string => match this { 38 | Error => "Error" 39 | Warning => "Warning" 40 | Note => "Note" 41 | } 42 | 43 | def display_line() { 44 | println("--------------------------------------------------------------------------------") 45 | } 46 | 47 | def display_message(type: MessageType, span: Span, msg: string) { 48 | display_line() 49 | println("%s: %s: %s", span.start.str(), type.str(), msg) 50 | display_line() 51 | } 52 | 53 | def display_message_span(type: MessageType, span: Span, msg: string) { 54 | let color = MessageType::to_color(type) 55 | let reset = "\x1b[0m" 56 | 57 | let filename = span.start.filename; 58 | let file = File::open(filename, "r") 59 | defer file.close() 60 | 61 | let contents = file.slurp() 62 | defer free(contents) 63 | 64 | let around_offset = 1 65 | let min_line = max(span.start.line - around_offset, 1) 66 | let max_line = span.end.line + around_offset 67 | 68 | display_message(type, span, msg) 69 | let lines = contents 70 | let cur = strsep(&lines, "\n") 71 | let line_no = 1 72 | while cur? and line_no <= max_line { 73 | if line_no >= min_line and line_no <= max_line { 74 | print("%4d | ", line_no) 75 | if line_no == span.start.line { 76 | let start_col = span.start.col - 1 77 | let end_col = span.end.col - 1 78 | if span.end.line != span.start.line { 79 | end_col = cur.len() 80 | } 81 | for let i = 0; i < start_col; i += 1 { 82 | print("%c", cur[i]) 83 | } 84 | print("%s", color) 85 | for let i = start_col; i < end_col; i += 1 { 86 | print("%c", cur[i]) 87 | } 88 | println("%s%s", reset, cur + end_col) 89 | println("%*s%s^ %s%s", start_col + 7, "", color, msg, reset) 90 | } else { 91 | println("%s", cur) 92 | } 93 | } 94 | line_no += 1 95 | cur = strsep(&lines, "\n") 96 | } 97 | } 98 | 99 | def Error::display(&this) { 100 | match .type { 101 | Standard => { 102 | display_message_span(MessageType::Error, .span1, .msg1) 103 | display_line() 104 | } 105 | WithNote => { 106 | display_message_span(MessageType::Error, .span1, .msg1) 107 | display_message(MessageType::Note, .span1, .msg2) 108 | } 109 | WithHint => { 110 | display_message_span(MessageType::Error, .span1, .msg1) 111 | display_message_span(MessageType::Note, .span2, .msg2) 112 | display_line() 113 | } 114 | } 115 | } 116 | 117 | def Error::panic(&this) exits { 118 | .display() 119 | exit(1) 120 | } 121 | 122 | def Error::new(span: Span, msg: string): &Error { 123 | let err = calloc(1, sizeof(Error)) as &Error 124 | err.type = ErrorType::Standard 125 | err.msg1 = msg 126 | err.span1 = span 127 | return err 128 | } 129 | 130 | def Error::new_note(span: Span, msg: string, note: string): &Error { 131 | let err = calloc(1, sizeof(Error)) as &Error 132 | err.type = ErrorType::WithNote 133 | err.msg1 = msg 134 | err.span1 = span 135 | err.msg2 = note 136 | return err 137 | } 138 | 139 | def Error::new_hint(span: Span, msg: string, span2: Span, hint: string): &Error { 140 | let err = calloc(1, sizeof(Error)) as &Error 141 | err.type = ErrorType::WithHint 142 | err.msg1 = msg 143 | err.span1 = span 144 | err.msg2 = hint 145 | err.span2 = span2 146 | return err 147 | } 148 | 149 | def display_error_messages(errors: &Vector, detail_level: i32) { 150 | let num_errors_env = get_environment_variable("AECOR_NUM_ERRORS") 151 | let max_num_errors = if num_errors_env? then num_errors_env.to_i32() else 10 152 | 153 | let num_errors = min(errors.size, max_num_errors) 154 | let first = true 155 | for let i = num_errors - 1; i >= 0; i -= 1 { 156 | let err = errors.at(i) as &Error 157 | 158 | match detail_level { 159 | 0 => println("%s: %s", err.span1.start.str(), err.msg1) 160 | 1 => display_message_span(MessageType::Error, err.span1, err.msg1) 161 | 2 => { 162 | if first then println("") 163 | first = false 164 | err.display() 165 | } 166 | else => panic("invalid detail level") 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /compiler/lexer.ae: -------------------------------------------------------------------------------- 1 | use "lib/vector.ae" 2 | use "compiler/tokens.ae" 3 | use "compiler/errors.ae" 4 | use "compiler/utils.ae" 5 | 6 | def is_hex_digit(c: char): bool { 7 | if is_digit(c) return true 8 | if 'a' <= c <= 'f' return true 9 | if 'A' <= c <= 'F' return true 10 | return false 11 | } 12 | 13 | struct Lexer { 14 | source: string 15 | source_len: i32 16 | i: i32 17 | loc: Location 18 | seen_newline: bool 19 | tokens: &Vector 20 | 21 | errors: &Vector 22 | } 23 | 24 | def Lexer::make(source: string, filename: string): Lexer { 25 | return Lexer( 26 | source, 27 | source_len: source.len(), 28 | i: 0, 29 | loc: Location(filename, 1, 1, 0), 30 | seen_newline: false, 31 | tokens: Vector::new(), 32 | errors: Vector::new() 33 | ) 34 | } 35 | 36 | def Lexer::push(&this, token: &Token) { 37 | token.seen_newline = .seen_newline 38 | .tokens.push(token) 39 | .seen_newline = false 40 | } 41 | 42 | def Lexer::push_type(&this, type: TokenType, len: i32) { 43 | let start_loc = .loc 44 | for let i = 0; i < len; i += 1 { 45 | .inc() 46 | } 47 | .push(Token::from_type(type, Span(start_loc, .loc))) 48 | } 49 | 50 | def Lexer::cur(&this): char => .source[.i] 51 | 52 | def Lexer::inc(&this) { 53 | match .cur() { 54 | '\n' => { 55 | .loc.line += 1 56 | .loc.col = 1 57 | .seen_newline = true 58 | } 59 | else => .loc.col += 1 60 | } 61 | .i += 1 62 | .loc.index += 1 63 | } 64 | 65 | def Lexer::peek(&this, offset: i32): char { 66 | if .cur() == '\0' { 67 | return .cur() 68 | } 69 | return .source[.i + 1] 70 | } 71 | 72 | def Lexer::lex_char_literal(&this) { 73 | let start_loc = .loc 74 | let start = .i + 1 75 | .inc() 76 | 77 | if .cur() == '\\' { 78 | .inc() 79 | } 80 | .inc() 81 | if .cur() != '\'' { 82 | .errors.push(Error::new(Span(.loc, .loc), "Expected ' after character literal")) 83 | } 84 | 85 | let len = .i - start 86 | let text = .source.substring(start, len) 87 | 88 | .inc() 89 | .push(Token::new(TokenType::CharLiteral, Span(start_loc, .loc), text)) 90 | } 91 | 92 | def Lexer::lex_string_literal(&this) { 93 | let start_loc = .loc 94 | let end_char = .cur() 95 | let start = .i + 1 96 | .inc() 97 | while .i < .source_len and .cur() != end_char { 98 | if .cur() == '\\' { 99 | .inc() 100 | } 101 | .inc() 102 | } 103 | 104 | let len = .i - start 105 | let text = .source.substring(start, len) 106 | .inc() 107 | 108 | if .i >= .source_len { 109 | .errors.push(Error::new(Span(.loc, .loc), "Unterminated string literal")) 110 | } 111 | 112 | if end_char == '`' { 113 | .push(Token::new(TokenType::FormatStringLiteral, Span(start_loc, .loc), text)) 114 | } else { 115 | .push(Token::new(TokenType::StringLiteral, Span(start_loc, .loc), text)) 116 | } 117 | } 118 | 119 | def Lexer::lex_int_literal_different_base(&this): &Token { 120 | let start_loc = .loc 121 | let start = .i 122 | .inc() 123 | match .cur() { 124 | 'x' => { 125 | .inc() 126 | while .i < .source_len and is_hex_digit(.cur()) { 127 | .inc() 128 | } 129 | } 130 | 'b' => { 131 | .inc() 132 | while .i < .source_len and .cur() == '0' or .cur() == '1' { 133 | .inc() 134 | } 135 | } 136 | 137 | // Should be unreachable 138 | else => {} 139 | } 140 | let len = .i - start 141 | let text = .source.substring(start, len) 142 | return Token::new(TokenType::IntLiteral, Span(start_loc, .loc), text) 143 | } 144 | 145 | def Lexer::lex_numeric_literal_helper(&this): &Token { 146 | let start_loc = .loc 147 | if .cur() == '0' { 148 | match .peek(1) { 149 | 'x' | 'b' => { 150 | return .lex_int_literal_different_base() 151 | } 152 | // Do nothing, fall through 153 | else => {} 154 | } 155 | } 156 | 157 | let start = .i 158 | let token_type: TokenType 159 | while is_digit(.cur()) { 160 | .inc() 161 | } 162 | if .cur() == '.' { 163 | .inc() 164 | while is_digit(.cur()) { 165 | .inc() 166 | } 167 | token_type = TokenType::FloatLiteral 168 | } else { 169 | token_type = TokenType::IntLiteral 170 | } 171 | let len = .i - start 172 | let text = .source.substring(start, len) 173 | 174 | return Token::new(token_type, Span(start_loc, .loc), text) 175 | } 176 | 177 | def Lexer::lex_numeric_literal(&this) { 178 | let token = .lex_numeric_literal_helper() 179 | 180 | // TODO: check for invalid suffixes 181 | if .cur() == 'u' or .cur() == 'i' or .cur() == 'f' { 182 | let start_loc = .loc 183 | let start = .i 184 | .inc() 185 | while .i < .source_len and is_digit(.cur()) { 186 | .inc() 187 | } 188 | let len = .i - start 189 | let suffix = .source.substring(start, len) 190 | token.suffix = Token::from_ident(suffix, Span(start_loc, .loc)) 191 | } 192 | 193 | .push(token) 194 | } 195 | 196 | def Lexer::lex(&this): &Vector { 197 | while .i < .source_len { 198 | let c = .cur() 199 | match c { 200 | ' ' | '\t' | '\v' | '\r' | '\b'| '\n' => { 201 | .inc() 202 | } 203 | ';' => .push_type(TokenType::Semicolon, len: 1) 204 | ',' => .push_type(TokenType::Comma, len: 1) 205 | '.' => .push_type(TokenType::Dot, len: 1) 206 | '(' => .push_type(TokenType::OpenParen, len: 1) 207 | ')' => .push_type(TokenType::CloseParen, len: 1) 208 | '[' => .push_type(TokenType::OpenSquare, len: 1) 209 | ']' => .push_type(TokenType::CloseSquare, len: 1) 210 | '{' => .push_type(TokenType::OpenCurly, len: 1) 211 | '}' => .push_type(TokenType::CloseCurly, len: 1) 212 | '@' => .push_type(TokenType::AtSign, len: 1) 213 | '%' => .push_type(TokenType::Percent, len: 1) 214 | '^' => .push_type(TokenType::Caret, len: 1) 215 | '&' => .push_type(TokenType::Ampersand, len: 1) 216 | '|' => .push_type(TokenType::Line, len: 1) 217 | '?' => .push_type(TokenType::Question, len: 1) 218 | '~' => .push_type(TokenType::Tilde, len: 1) 219 | '!' => match .peek(1) { 220 | '=' => .push_type(TokenType::NotEquals, len: 2) 221 | else => .push_type(TokenType::Exclamation, len: 1) 222 | } 223 | ':' => match .peek(1) { 224 | ':' => .push_type(TokenType::ColonColon, len: 2) 225 | else => .push_type(TokenType::Colon, len: 1) 226 | } 227 | '=' => match .peek(1) { 228 | '=' => .push_type(TokenType::EqualEquals, len: 2) 229 | '>' => .push_type(TokenType::FatArrow, len: 2) 230 | else => .push_type(TokenType::Equals, len: 1) 231 | } 232 | '*' => match .peek(1) { 233 | '=' => .push_type(TokenType::StarEquals, len: 2) 234 | else => .push_type(TokenType::Star, len: 1) 235 | } 236 | '+' => match .peek(1) { 237 | '=' => .push_type(TokenType::PlusEquals, len: 2) 238 | else => .push_type(TokenType::Plus, len: 1) 239 | } 240 | '-' => match .peek(1) { 241 | '=' => .push_type(TokenType::MinusEquals, len: 2) 242 | else => .push_type(TokenType::Minus, len: 1) 243 | } 244 | '<' => match .peek(1) { 245 | '=' => .push_type(TokenType::LessThanEquals, len: 2) 246 | '<' => .push_type(TokenType::LessThanLessThan, len: 2) 247 | else => .push_type(TokenType::LessThan, len: 1) 248 | } 249 | '>' => match .peek(1) { 250 | '=' => .push_type(TokenType::GreaterThanEquals, len: 2) 251 | '>' => .push_type(TokenType::GreaterThanGreaterThan, len: 2) 252 | else => .push_type(TokenType::GreaterThan, len: 1) 253 | } 254 | '/' => match .peek(1) { 255 | '/' => { 256 | .inc() 257 | while .i < .source_len and .cur() != '\n' { 258 | .inc() 259 | } 260 | } 261 | '=' => .push_type(TokenType::SlashEquals, len: 2) 262 | else => .push_type(TokenType::Slash, len: 1) 263 | } 264 | '\'' => .lex_char_literal() 265 | '"' | '`' => .lex_string_literal() 266 | else => { 267 | let start_loc = .loc 268 | 269 | if is_digit(c) { 270 | .lex_numeric_literal() 271 | 272 | } else if is_alpha(c) or c == '_' { 273 | let start = .i 274 | while is_alnum(.cur()) or .cur() == '_' { 275 | .inc() 276 | } 277 | let len = .i - start 278 | let text = .source.substring(start, len) 279 | 280 | .push(Token::from_ident(text, Span(start_loc, .loc))) 281 | 282 | } else { 283 | .errors.push(Error::new(Span(.loc, .loc), `Unrecognized char in lexer: '{c}'`)) 284 | .inc() 285 | } 286 | } 287 | } 288 | } 289 | 290 | // We can assume EOF acts like a newline 291 | .seen_newline = true 292 | .push_type(TokenType::EOF, len: 0) 293 | return .tokens 294 | } 295 | -------------------------------------------------------------------------------- /compiler/main.ae: -------------------------------------------------------------------------------- 1 | use "compiler/lexer.ae" 2 | use "compiler/parser.ae" 3 | use "compiler/typecheck.ae" 4 | use "compiler/codegen.ae" 5 | use "compiler/errors.ae" 6 | 7 | def usage(code: i32) { 8 | println("--------------------------------------------------------") 9 | println("Usage: ./aecor [options] ") 10 | println("Options:") 11 | println(" -o path Output executable (default: ./out)") 12 | println(" -c path Output C code (default: {out}.c)") 13 | println(" -e0 Minimal one-line errors") 14 | println(" -e1 Error messages with source code (default)") 15 | println(" -e2 Error messages with source / hints") 16 | println(" -s Silent mode (no debug output)") 17 | println(" -n Don't compile C code (default: false)") 18 | println(" -d Emit debug information (default: false)") 19 | println(" -l Library path (root of aecor repo)") 20 | println(" (Default: working directory)") 21 | println("--------------------------------------------------------") 22 | exit(code) 23 | } 24 | 25 | def main(argc: i32, argv: &string) { 26 | let exec_path = "./out" 27 | let c_path = null as string 28 | let filename = null as string 29 | let compile_c = true 30 | let silent = false 31 | let lib_path = null as string 32 | let debug = false 33 | let error_level = 1 34 | 35 | for let i = 1; i < argc; i += 1 { 36 | match argv[i] { 37 | "-h" => usage(code: 0) 38 | "-s" => silent = true 39 | "-d" => debug = true 40 | "-n" => compile_c = false 41 | "-o" => { 42 | i += 1 43 | exec_path = argv[i] 44 | } 45 | "-l" => { 46 | i += 1 47 | lib_path = argv[i] 48 | } 49 | "-c" => { 50 | i += 1 51 | c_path = argv[i] 52 | } 53 | "-e0" => error_level = 0 54 | "-e1" => error_level = 1 55 | "-e2" => error_level = 2 56 | else => { 57 | if argv[i][0] == '-' { 58 | println("Unknown option: %s", argv[i]) 59 | usage(1) 60 | } else if not filename? { 61 | filename = argv[i] 62 | } else { 63 | println("Unknown option/argument: '%s'", argv[i]) 64 | usage(code: 1) 65 | } 66 | } 67 | } 68 | } 69 | 70 | if not filename? { 71 | println("No file specified") 72 | usage(code: 1) 73 | } 74 | 75 | if not c_path? { 76 | c_path = `{exec_path}.c` 77 | } 78 | 79 | let parser = Parser::new(filename) 80 | if lib_path? { 81 | parser.add_include_dir(lib_path) 82 | } 83 | 84 | let program = Program::new() 85 | parser.include_prelude(program) 86 | parser.include_file(program, filename) 87 | 88 | let checker = TypeChecker::new() 89 | checker.check_program(program) 90 | 91 | if program.errors.size > 0 { 92 | display_error_messages(program.errors, error_level) 93 | exit(1) 94 | } 95 | 96 | let generator = CodeGenerator::make(debug) 97 | let c_code = generator.gen_program(program) 98 | 99 | if program.errors.size > 0 { 100 | display_error_messages(program.errors, error_level) 101 | exit(1) 102 | } 103 | 104 | let out_file = File::open(c_path, "w") 105 | out_file.puts(c_code) 106 | out_file.close() 107 | 108 | if not compile_c { 109 | return 0 110 | } 111 | 112 | let cmdbuf = calloc(1, 1024) as string 113 | cmdbuf.concat(`gcc -o {exec_path} {c_path}`) 114 | for let i = 0; i < program.c_flags.size; i += 1 { 115 | let flag = program.c_flags.at(i) as string 116 | cmdbuf.concat(" ") 117 | cmdbuf.concat(flag) 118 | } 119 | if debug { 120 | cmdbuf.concat(" -ggdb3") 121 | } 122 | 123 | if not silent { 124 | println("[+] %s", cmdbuf) 125 | } 126 | let code = system(cmdbuf) 127 | if code != 0 { 128 | println("[-] Compilation failed") 129 | exit(code) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /compiler/tokens.ae: -------------------------------------------------------------------------------- 1 | use "lib/span.ae" 2 | 3 | struct Token { 4 | type: TokenType 5 | span: Span 6 | text: string 7 | suffix: &Token 8 | seen_newline: bool 9 | } 10 | 11 | def Token::new(type: TokenType, span: Span, text: string): &Token { 12 | let tok = calloc(1, sizeof(Token)) as &Token 13 | *tok = Token(type, span, text, suffix: null, seen_newline: false) 14 | return tok 15 | } 16 | 17 | def Token::from_type(type: TokenType, span: Span): &Token => Token::new(type, span, "") 18 | 19 | def Token::from_ident(text: string, span: Span): &Token { 20 | let type = TokenType::from_text(text) 21 | return Token::new(type, span, text) 22 | } 23 | 24 | def Token::str(&this): string => `{.span.str()}: {.type.str()}` 25 | 26 | enum TokenType { 27 | // Keywords 28 | And 29 | As 30 | Bool 31 | Break 32 | Char 33 | Const 34 | Continue 35 | Def 36 | Defer 37 | Else 38 | Enum 39 | Extern 40 | False 41 | F32 42 | F64 43 | For 44 | Fn 45 | I8 46 | I16 47 | I32 48 | I64 49 | If 50 | Let 51 | Match 52 | Null 53 | Not 54 | Or 55 | Return 56 | SizeOf 57 | String 58 | Struct 59 | True 60 | Then 61 | U8 62 | U16 63 | U32 64 | U64 65 | UntypedPtr 66 | Union 67 | Use 68 | Void 69 | Yield 70 | While 71 | 72 | // Other tokens 73 | AtSign 74 | Ampersand 75 | Backtick 76 | Caret 77 | CharLiteral 78 | CloseCurly 79 | CloseParen 80 | CloseSquare 81 | Colon 82 | ColonColon 83 | Comma 84 | Dot 85 | EOF 86 | Equals 87 | EqualEquals 88 | Exclamation 89 | FatArrow 90 | FloatLiteral 91 | FormatStringLiteral 92 | GreaterThan 93 | GreaterThanGreaterThan 94 | GreaterThanEquals 95 | Identifier 96 | IntLiteral 97 | LessThan 98 | LessThanLessThan 99 | LessThanEquals 100 | Line 101 | Minus 102 | MinusEquals 103 | NotEquals 104 | Newline // Not an actual token, but nice to have this here 105 | OpenCurly 106 | OpenParen 107 | OpenSquare 108 | Percent 109 | Plus 110 | PlusEquals 111 | Question 112 | Semicolon 113 | Slash 114 | SlashEquals 115 | Star 116 | StarEquals 117 | StringLiteral 118 | Tilde 119 | } 120 | 121 | def TokenType::from_text(text: string): TokenType => match text { 122 | "and" => TokenType::And 123 | "as" => TokenType::As 124 | "bool" => TokenType::Bool 125 | "break" => TokenType::Break 126 | "char" => TokenType::Char 127 | "const" => TokenType::Const 128 | "continue" => TokenType::Continue 129 | "def" => TokenType::Def 130 | "defer" => TokenType::Defer 131 | "else" => TokenType::Else 132 | "enum" => TokenType::Enum 133 | "extern" => TokenType::Extern 134 | "false" => TokenType::False 135 | "f32" => TokenType::F32 136 | "f64" => TokenType::F64 137 | "for" => TokenType::For 138 | "fn" => TokenType::Fn 139 | "i8" => TokenType::I8 140 | "i16" => TokenType::I16 141 | "i32" => TokenType::I32 142 | "i64" => TokenType::I64 143 | "if" => TokenType::If 144 | "let" => TokenType::Let 145 | "match" => TokenType::Match 146 | "not" => TokenType::Not 147 | "null" => TokenType::Null 148 | "or" => TokenType::Or 149 | "return" => TokenType::Return 150 | "sizeof" => TokenType::SizeOf 151 | "string" => TokenType::String 152 | "struct" => TokenType::Struct 153 | "true" => TokenType::True 154 | "then" => TokenType::Then 155 | "u8" => TokenType::U8 156 | "u16" => TokenType::U16 157 | "u32" => TokenType::U32 158 | "u64" => TokenType::U64 159 | "untyped_ptr" => TokenType::UntypedPtr 160 | "union" => TokenType::Union 161 | "use" => TokenType::Use 162 | "void" => TokenType::Void 163 | "yield" => TokenType::Yield 164 | "while" => TokenType::While 165 | else => TokenType::Identifier 166 | } 167 | 168 | def TokenType::str(this): string => match this { 169 | // Keywords 170 | And => "and" 171 | As => "as" 172 | Bool => "bool" 173 | Break => "break" 174 | Char => "char" 175 | Const => "const" 176 | Continue => "continue" 177 | Def => "def" 178 | Defer => "defer" 179 | Else => "else" 180 | Enum => "enum" 181 | Extern => "extern" 182 | False => "false" 183 | F32 => "f32" 184 | F64 => "f64" 185 | For => "for" 186 | Fn => "fn" 187 | I8 => "i8" 188 | I16 => "i16" 189 | I32 => "i32" 190 | I64 => "i64" 191 | If => "if" 192 | Let => "let" 193 | Match => "match" 194 | Not => "not" 195 | Null => "null" 196 | Or => "or" 197 | Return => "return" 198 | SizeOf => "sizeof" 199 | String => "string" 200 | Struct => "struct" 201 | True => "true" 202 | Then => "then" 203 | U8 => "u8" 204 | U16 => "u16" 205 | U32 => "u32" 206 | U64 => "u64" 207 | UntypedPtr => "untyped_ptr" 208 | Union => "union" 209 | Use => "use" 210 | Void => "void" 211 | Yield => "yield" 212 | While => "while" 213 | 214 | // Others 215 | else => .dbg() 216 | } 217 | -------------------------------------------------------------------------------- /compiler/types.ae: -------------------------------------------------------------------------------- 1 | use "compiler/ast.ae" 2 | use "compiler/tokens.ae" 3 | 4 | enum BaseType { 5 | Char 6 | Bool 7 | Void 8 | I8 9 | I16 10 | I32 11 | I64 12 | U8 13 | U16 14 | U32 15 | U64 16 | F32 17 | F64 18 | 19 | Pointer 20 | Structure 21 | Function 22 | Method 23 | Array 24 | 25 | Error 26 | } 27 | 28 | def BaseType::str(this): string => match this { 29 | Char => "char" 30 | Bool => "bool" 31 | Void => "void" 32 | I8 => "i8" 33 | I16 => "i16" 34 | I32 => "i32" 35 | I64 => "i64" 36 | U8 => "u8" 37 | U16 => "u16" 38 | U32 => "u32" 39 | U64 => "u64" 40 | F32 => "f32" 41 | F64 => "f64" 42 | else => .dbg() 43 | } 44 | 45 | struct Type { 46 | base: BaseType 47 | ptr: &Type 48 | name: string 49 | span: Span 50 | size_expr: &AST 51 | 52 | struct_def: &Structure 53 | 54 | func_def: &Function 55 | return_type: &Type 56 | params: &Vector // Vector<&Variable> 57 | } 58 | 59 | def Type::new(base: BaseType, span: Span): &Type { 60 | let type = calloc(1, sizeof(Type)) as &Type 61 | type.base = base 62 | type.span = span 63 | return type 64 | } 65 | 66 | def Type::new_link(base: BaseType, next: &Type, span: Span): &Type { 67 | let type = Type::new(base, span) 68 | type.ptr = next 69 | 70 | // This is a hack to attach methods to strings 71 | if base == BaseType::Pointer and next? and next.base == BaseType::Char { 72 | type.name = "string" 73 | } 74 | return type 75 | } 76 | 77 | def Type::ptr_to(base: BaseType, span: Span): &Type { 78 | let next = Type::new(base, span) 79 | return Type::new_link(BaseType::Pointer, next, span) 80 | } 81 | 82 | def Type::is_struct_or_ptr(&this): bool { 83 | if .base == BaseType::Structure return true 84 | if .base != BaseType::Pointer return false 85 | return .ptr.base == BaseType::Structure 86 | } 87 | 88 | def Type::is_integer(&this): bool => match .base { 89 | I8 | I16 | I32 | I64 | 90 | U8 | U16 | U32 | U64 => true 91 | else => false 92 | } 93 | 94 | def Type::is_float(&this): bool => .base == BaseType::F32 or .base == BaseType::F64 95 | 96 | def Type::is_numeric(&this): bool => match .base { 97 | I8 | I16 | I32 | I64 | 98 | U8 | U16 | U32 | U64 | 99 | F32 | F64 => true 100 | else => false 101 | } 102 | 103 | def Type::is_numeric_or_char(&this): bool => .is_numeric() or .base == BaseType::Char 104 | 105 | def Type::eq(&this, other: &Type): bool { 106 | if (this == null and other == null) return true 107 | if (this == null or other == null) return false 108 | if .base != other.base return false 109 | 110 | match .base { 111 | // Not _technically_ right, but we shouldn't really ever be comparing methods 112 | Method | Error => return false 113 | // FIXME: have some way of checking the size... 114 | Array => return .ptr.eq(other.ptr) 115 | Function => { 116 | if not .return_type.eq(other.return_type) return false 117 | if .params.size != other.params.size return false 118 | for let i = 0; i < .params.size; i += 1 { 119 | let a = .params.at(i) as &Variable 120 | let b = other.params.at(i) as &Variable 121 | if not a.type.eq(b.type) return false 122 | } 123 | return true 124 | } 125 | Structure => return .name.eq(other.name) 126 | Pointer => { 127 | // EXPERIMENTAL: `untyped_ptr` is equivalent to any pointer type. 128 | if .ptr.base == BaseType::Void or other.ptr.base == BaseType::Void { 129 | return true 130 | } 131 | return .ptr.eq(other.ptr) 132 | } 133 | else => return true 134 | } 135 | } 136 | 137 | def Type::str(&this): string => match .base { 138 | Pointer => `&{.ptr.str()}` 139 | Array => `[{.ptr.str()}]` 140 | Structure => .name 141 | Function => "" 142 | Method => "" 143 | else => .base.str() 144 | } 145 | 146 | def Type::is_string(&this): bool => .base == BaseType::Pointer and .ptr.base == BaseType::Char 147 | 148 | def Type::decay_array(&this): &Type { 149 | if .base != BaseType::Array return this 150 | return Type::new_link(BaseType::Pointer, .ptr, .span) 151 | } 152 | 153 | def Type::is_enum(&this): bool => .base == BaseType::Structure and .struct_def? and .struct_def.is_enum 154 | def Type::is_struct(&this): bool => .base == BaseType::Structure and not .is_enum() 155 | def Type::is_array(&this): bool => .base == BaseType::Array 156 | -------------------------------------------------------------------------------- /compiler/utils.ae: -------------------------------------------------------------------------------- 1 | use "lib/vector.ae" 2 | use "lib/math.ae" 3 | use "compiler/tokens.ae" 4 | 5 | def strsep(s: &string, delim: string): string extern 6 | 7 | def edit_distance(str1: string, str2: string): i32 { 8 | let n = str1.len() 9 | let m = str2.len() 10 | let stride = m + 1 11 | 12 | // The stack _should_ be large enough to hold the entire matrix. 13 | let d: [[i32; m + 1]; n + 1] 14 | 15 | for let i = 0; i <= n; i += 1 { 16 | d[i][0] = i 17 | } 18 | for let j = 0; j <= m; j += 1 { 19 | d[0][j] = j 20 | } 21 | for let i = 1; i <= n; i += 1 { 22 | for let j = 1; j <= m; j += 1 { 23 | let x = d[i - 1][j] + 1 24 | let y = d[i][j - 1] + 1 25 | let z: i32 26 | if str1[i - 1] == str2[j - 1] { 27 | z = d[i - 1][j - 1] 28 | } else { 29 | z = d[i - 1][j - 1] + 1 30 | } 31 | d[i][j] = min(x, min(y, z)) 32 | } 33 | } 34 | let result = d[n][m] 35 | return result 36 | } 37 | 38 | def find_word_suggestion(s: string, options: &Vector): string { 39 | let threshold = 5 // edit distance threshold 40 | 41 | if options.size == 0 return null 42 | 43 | let closest = options.at(0) as string 44 | let closest_distance = edit_distance(s, closest) 45 | 46 | for let i = 1; i < options.size; i += 1 { 47 | let option = options.at(i) as string 48 | let distance = edit_distance(s, option) 49 | if distance < closest_distance { 50 | closest = option 51 | closest_distance = distance 52 | } 53 | } 54 | if closest_distance > threshold return null 55 | return closest 56 | } 57 | -------------------------------------------------------------------------------- /examples/daecor/bezier.ae: -------------------------------------------------------------------------------- 1 | use "lib/daecor/all.ae" 2 | use "lib/vector.ae" 3 | 4 | const X = 5 5 | const Y = X * 8 6 | 7 | struct Circle { 8 | pos: Vec2i 9 | radius: i32 10 | } 11 | 12 | struct Foo { 13 | lvalues: [i32; Y] 14 | } 15 | 16 | def Circle::new(pos: Vec2i, radius: i32): &Circle { 17 | let circle = calloc(1, sizeof(Circle)) as &Circle 18 | *circle = Circle(pos, radius) 19 | return circle 20 | } 21 | 22 | let circles: &Vector 23 | 24 | def draw() { 25 | set_color(Color::Red()) 26 | for let i = 0; i < circles.size; i += 1 { 27 | let circle = circles.at(i) as &Circle 28 | fill_circle(circle.pos, circle.radius) 29 | } 30 | let points: [Vec2i; circles.size] 31 | for let i = 0; i < circles.size; i += 1 { 32 | let circle = circles.at(i) as &Circle 33 | points[i] = circle.pos 34 | } 35 | draw_bezier(circles.size, points, 10) 36 | } 37 | 38 | def circle_hover_idx(): i32 { 39 | let mpos = mouse.pos() 40 | for let i = 0; i < circles.size; i += 1 { 41 | let circle = circles.at(i) as &Circle 42 | let thresh = circle.radius + 10 43 | if circle.pos.sub(mpos).length_sq() < thresh * thresh { 44 | return i 45 | } 46 | } 47 | return -1 48 | } 49 | 50 | def main() { 51 | init() 52 | circles = Vector::new() 53 | 54 | let dragging = false 55 | let which = -1 56 | 57 | while true { 58 | let t = frame_start() 59 | 60 | if not dragging { 61 | let cur = circle_hover_idx() 62 | if cur != which { 63 | if cur < 0 { 64 | set_cursor(CursorType::Arrow) 65 | } else { 66 | set_cursor(CursorType::Hand) 67 | } 68 | which = cur 69 | } 70 | } 71 | 72 | if mouse.pressed(MouseButton::Left) { 73 | if which >= 0 { 74 | dragging = true 75 | } else { 76 | let circle = Circle::new(mouse.pos(), 10) 77 | circles.push(circle) 78 | } 79 | } 80 | 81 | if mouse.released(MouseButton::Left) { 82 | dragging = false 83 | } 84 | 85 | if dragging { 86 | let circle = circles.at(which) as &Circle 87 | circle.pos = mouse.pos() 88 | } 89 | 90 | if mouse.pressed(MouseButton::Right) { 91 | if circles.size > 0 { 92 | free(circles.pop()) 93 | } 94 | } 95 | 96 | draw() 97 | frame_end(t); 98 | } 99 | return 0; 100 | } -------------------------------------------------------------------------------- /examples/daecor/rope.ae: -------------------------------------------------------------------------------- 1 | use "lib/daecor/all.ae" 2 | 3 | const NUM_NODES = 20 4 | const NODE_RADIUS = 10.0 5 | const NODE_DISTANCE = 30.0 6 | const LINE_THICKNESS = 3.0 7 | 8 | struct Rope { 9 | pos: [Vec2f; NUM_NODES] 10 | vel: [Vec2f; NUM_NODES] 11 | dragging: bool 12 | drag_index: i32 13 | hover_index: i32 14 | } 15 | 16 | def Rope::update_pair(&this, a: i32, b: i32) { 17 | let diff = .pos[a].sub(.pos[b]) 18 | let dist = diff.length() 19 | let dir = diff.normalized() 20 | .vel[a] = dir.multf(NODE_DISTANCE - dist) 21 | 22 | for let i = 0; i < NUM_NODES; i += 1 { 23 | if i != a { 24 | let diff = .pos[a].sub(.pos[i]) 25 | let dist = diff.length() 26 | if dist < NODE_RADIUS * 2.0 { 27 | .vel[a] = .vel[a].add(diff.multf((NODE_RADIUS * 2.0 - dist)/dist)) 28 | } 29 | } 30 | } 31 | } 32 | 33 | def Rope::update(&this) { 34 | .vel[.drag_index] = Vec2f(0.0, 0.0) 35 | for let i = .drag_index+1; i < NUM_NODES; i += 1 36 | .update_pair(i, i-1) 37 | for let i = .drag_index-1; i >= 0; i -= 1 38 | .update_pair(i, i+1) 39 | for let i = 0; i < NUM_NODES; i += 1 40 | .pos[i] = .pos[i].add(.vel[i]) 41 | } 42 | 43 | def Rope::draw(&this) { 44 | set_color(Color::White()) 45 | fill_screen() 46 | 47 | set_color(Color::rgb(100, 100, 100)) 48 | for let x = 1; x < NUM_NODES; x += 1{ 49 | let dir = .pos[x].sub(.pos[x-1]) 50 | let a = Vec2f(-dir.y, dir.x).normalized().multf(LINE_THICKNESS) 51 | let p1 = .pos[x-1].add(a).to_i() 52 | let p2 = .pos[x-1].sub(a).to_i() 53 | let p3 = .pos[x].sub(a).to_i() 54 | let p4 = .pos[x].add(a).to_i() 55 | 56 | fill_tri(p1, p2, p3) 57 | fill_tri(p1, p3, p4) 58 | } 59 | 60 | for let x = 0; x < NUM_NODES; x += 1{ 61 | set_color( 62 | if x == .hover_index or (x == .drag_index and .dragging) then 63 | Color::Green() 64 | else 65 | Color::Red() 66 | ) 67 | fill_circle(.pos[x].to_i(), NODE_RADIUS as i32) 68 | } 69 | } 70 | 71 | def Rope::hovered_idx(&this): i32 { 72 | let mouse_pos = mouse.curr.pos.to_f() 73 | for let x = 0; x < NUM_NODES; x += 1{ 74 | let p = .pos[x] 75 | if mouse_pos.sub(p).length_sq() < NODE_RADIUS * NODE_RADIUS { 76 | return x 77 | } 78 | } 79 | return -1 80 | } 81 | 82 | def main() { 83 | init() 84 | 85 | let rope: Rope 86 | for let x = 0; x < NUM_NODES; x += 1{ 87 | let size = get_window_size() 88 | rope.pos[x] = Vec2f(rand01() * size.x as f32, rand01() * size.y as f32) 89 | rope.vel[x] = Vec2f(0.0, 0.0) 90 | } 91 | rope.dragging = false 92 | rope.drag_index = 0 93 | 94 | while true { 95 | let t = frame_start() 96 | if keys.pressed(Key::Escape) or keys.pressed(Key::Q) break 97 | 98 | let idx = rope.hovered_idx() 99 | rope.hover_index = idx 100 | 101 | if mouse.pressed(MouseButton::Left) { 102 | if not rope.dragging and idx >= 0 { 103 | rope.dragging = true 104 | rope.drag_index = idx 105 | } 106 | } 107 | 108 | if mouse.released(MouseButton::Left) { 109 | rope.dragging = false 110 | } 111 | 112 | if rope.dragging { 113 | rope.pos[rope.drag_index] = mouse.curr.pos.to_f() 114 | } 115 | if rope.hover_index >= 0 or rope.dragging { 116 | set_cursor(CursorType::Hand) 117 | } else { 118 | set_cursor(CursorType::Arrow) 119 | } 120 | 121 | rope.update() 122 | rope.draw() 123 | frame_end(t); 124 | } 125 | return 0; 126 | } -------------------------------------------------------------------------------- /examples/daecor/snake.ae: -------------------------------------------------------------------------------- 1 | // Ported from https://github.com/DogeSSBM/SnakeDoge 2 | 3 | use "lib/daecor/all.ae" 4 | 5 | enum Square { SU, SR, SD, SL, SF, A, E } 6 | 7 | def Square::is_snake(this): bool => match this { 8 | SU | SR | SD | SL | SF => true 9 | else => false 10 | } 11 | 12 | struct Board { 13 | scale: i32 14 | size: Vec2i 15 | grid: &&Square 16 | } 17 | 18 | struct Snake { 19 | head: Vec2i 20 | dir: Direction 21 | } 22 | 23 | def Board::draw(&this) { 24 | for let y = 0; y < .size.y; y += 1 { 25 | for let x = 0; x < .size.x; x += 1 { 26 | match .grid[x][y] { 27 | SU | SR | SD | SL | SF => { 28 | set_color(Color::Green()) 29 | fill_square(Vec2i(x, y).multi(.scale).addi(.scale / 2), .scale) 30 | } 31 | A => { 32 | set_color(Color::Red()) 33 | fill_square(Vec2i(x, y).multi(.scale).addi(.scale / 2), .scale) 34 | } 35 | else => { 36 | set_color(Color::Grey()) 37 | draw_square(Vec2i(x, y).multi(.scale).addi(.scale / 2), .scale) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | def Board::place_apple(&this) { 45 | while true { 46 | let x = randint() % .size.x 47 | let y = randint() % .size.y 48 | if not .grid[x][y].is_snake() { 49 | .grid[x][y] = Square::A 50 | break 51 | } 52 | } 53 | } 54 | 55 | def Board::next_snake(&this, pos: Vec2i): Vec2i { 56 | let cur = .grid[pos.x][pos.y] 57 | return match cur { 58 | SU | SR | SD | SL => pos.shift(cur as Direction, 1) 59 | SF | A | E => pos 60 | } 61 | } 62 | 63 | def Board::lose(&this) exits { 64 | for let x = 0; x < .size.x; x += 1 { 65 | free(.grid[x]) 66 | } 67 | free(.grid) 68 | println("Game over") 69 | exit(0) 70 | } 71 | 72 | def Board::move_snake(&this, snake: Snake): Vec2i { 73 | let ret = snake.head.shift(snake.dir, 1) 74 | 75 | let x = (ret.x + .size.x) % .size.x 76 | let y = (ret.y + .size.y) % .size.y 77 | ret.x = x 78 | ret.y = y 79 | if (.grid[ret.x][ret.y].is_snake()) { 80 | .lose() 81 | } 82 | let is_apple = .grid[ret.x][ret.y] == Square::A 83 | if not is_apple { 84 | let pos: Vec2i 85 | let next = snake.head; 86 | while true { 87 | pos = next 88 | next = .next_snake(pos) 89 | let x = (next.x + .size.x) % .size.x 90 | let y = (next.y + .size.y) % .size.y 91 | next.x = x 92 | next.y = y 93 | if .grid[next.x][next.y] == Square::SF { 94 | break 95 | } 96 | } 97 | .grid[next.x][next.y] = Square::E 98 | .grid[pos.x][pos.y] = Square::SF 99 | } 100 | .grid[ret.x][ret.y] = snake.dir.inv() as Square 101 | if is_apple { 102 | .place_apple() 103 | } 104 | return ret 105 | } 106 | 107 | def read_dir_keys(dir_keys: &bool) { 108 | dir_keys[0] = dir_keys[0] or keys.pressed(Key::Up) 109 | dir_keys[1] = dir_keys[1] or keys.pressed(Key::Right) 110 | dir_keys[2] = dir_keys[2] or keys.pressed(Key::Down) 111 | dir_keys[3] = dir_keys[3] or keys.pressed(Key::Left) 112 | } 113 | 114 | 115 | def get_dir(cur: Direction, dir_keys: &bool): Direction { 116 | let ret = cur 117 | for let i = 0; i < 4; i += 1 { 118 | let dir = i as Direction 119 | if (dir_keys[i] and 120 | not dir_keys[dir.inv() as i32] and 121 | dir != cur.inv()) { 122 | ret = dir 123 | } 124 | } 125 | return ret 126 | } 127 | 128 | def main() { 129 | init() 130 | let board: Board 131 | board.scale = 30 132 | board.size = get_window_size().divi(board.scale) 133 | board.grid = calloc(sizeof(&Square), board.size.x) 134 | 135 | for let x = 0; x < board.size.x; x += 1{ 136 | board.grid[x] = calloc(sizeof(Square), board.size.y) 137 | for let y = 0; y < board.size.y; y += 1 { 138 | board.grid[x][y] = Square::E 139 | } 140 | } 141 | 142 | let snake = Snake(board.size.divi(2), Direction::Left) 143 | board.grid[snake.head.x][snake.head.y] = Square::SR; 144 | board.grid[snake.head.x][snake.head.y] = Square::SF; 145 | board.place_apple() 146 | 147 | let frameCount = 0; 148 | let dir_keys: [bool; 4] 149 | set_memory(dir_keys, 0 as u8, sizeof(bool) * 4) 150 | 151 | while true { 152 | let t = frame_start() 153 | 154 | if keys.pressed(Key::Escape) or keys.pressed(Key::Q) { 155 | break 156 | } 157 | 158 | read_dir_keys(dir_keys) 159 | frameCount += 1 160 | if frameCount > gfx.FPS / 20 { 161 | frameCount = 0 162 | snake.dir = get_dir(snake.dir, dir_keys) 163 | snake.head = board.move_snake(snake) 164 | set_memory(dir_keys, 0 as u8, sizeof(bool) * 4) 165 | } 166 | board.draw() 167 | 168 | frame_end(t); 169 | } 170 | return 0; 171 | } -------------------------------------------------------------------------------- /examples/daecor/tictactoe.ae: -------------------------------------------------------------------------------- 1 | use "lib/daecor/all.ae" 2 | 3 | enum Marker { Empty, X, O, } 4 | struct Board { cells: [[Marker; 3]; 3] } 5 | 6 | def hovered_cell(): Vec2i { 7 | let length = get_window_size().min() 8 | let stride = length / 3 9 | let top_left = get_window_mid().subi(length / 2) 10 | 11 | let pos = mouse.pos().sub(top_left) 12 | if pos.x < 0 or pos.y < 0 { 13 | return Vec2i(-1, -1) 14 | } 15 | 16 | let cell = pos.divi(stride) 17 | if cell.x < 0 or cell.x > 2 or cell.y < 0 or cell.y > 2 { 18 | return Vec2i(-1, -1) 19 | } 20 | return cell 21 | } 22 | 23 | def Board::draw(&this) { 24 | let length = get_window_size().min() 25 | let stride = length / 3 26 | 27 | set_font_size(stride) 28 | let thickness = 8 29 | let top_left = get_window_mid().subi(length / 2) 30 | 31 | let cell = hovered_cell() 32 | if cell.x >= 0 and cell.y >= 0 { 33 | set_color(Color(100, 100, 100, 255)) 34 | let corner = top_left.add(cell.multi(stride)) 35 | let rect = Rect(corner.x, corner.y, stride, stride) 36 | fill_rect(rect) 37 | } 38 | 39 | set_color(Color::White()) 40 | for let i = 1; i < 3; i += 1 { 41 | let top = top_left.add(Vec2i(stride * i, 0)) 42 | let left = top_left.add(Vec2i(0, stride * i)) 43 | 44 | fill_rect(Rect(top.x, top.y, thickness, length)) 45 | fill_rect(Rect(left.x, left.y, length, thickness)) 46 | } 47 | 48 | for let x = 0; x < 3; x += 1 { 49 | for let y = 0; y < 3; y += 1 { 50 | let loc = top_left.add(Vec2i(x, y).multi(stride)).addi(stride / 2) 51 | match .cells[x][y] { 52 | Empty => {} 53 | X => { 54 | set_text_color(Color::Red()) 55 | draw_text_centered("X", loc) 56 | } 57 | O => { 58 | set_text_color(Color::Blue()) 59 | draw_text_centered("O", loc) 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | 67 | def main() { 68 | init() 69 | 70 | let board: Board 71 | set_memory(&board, 0 as u8, sizeof(Board)) 72 | 73 | let player = Marker::X 74 | let sound = Sound::load("lib/daecor/assets/pew.wav") 75 | 76 | while true { 77 | let T = frame_start() 78 | if keys.pressed(Key::Escape) or keys.pressed(Key::Q) { 79 | break 80 | } 81 | 82 | if keys.pressed(Key::Space) or keys.pressed(Key::R) { 83 | for let x = 0; x < 3; x += 1 { 84 | for let y = 0; y < 3; y += 1 { 85 | board.cells[x][y] = Marker::Empty 86 | } 87 | } 88 | } 89 | 90 | if mouse.pressed(MouseButton::Left) { 91 | let cell = hovered_cell() 92 | if cell.x >= 0 and cell.y >= 0 { 93 | if board.cells[cell.x][cell.y] == Marker::Empty { 94 | board.cells[cell.x][cell.y] = player 95 | player = if player == Marker::X then Marker::O else Marker::X 96 | sound.play_once() 97 | } 98 | } 99 | } 100 | 101 | board.draw() 102 | frame_end(T) 103 | } 104 | return 0 105 | } -------------------------------------------------------------------------------- /examples/glut_orbit.ae: -------------------------------------------------------------------------------- 1 | use "lib/glut.ae" 2 | 3 | 4 | /// Math prereqs 5 | @compiler c_include "math.h" 6 | @compiler c_flag "-lm" 7 | 8 | def cos(x: f32): f32 extern 9 | def sin(x: f32): f32 extern 10 | 11 | def myWireSphere(radius: f32, slices: i32, stacks: i32) { 12 | glPushMatrix() 13 | glRotatef(-90.0, 1.0, 0.0, 0.0) 14 | glutWireSphere(radius, slices, stacks) 15 | glPopMatrix() 16 | } 17 | 18 | let year: i32 = 0 19 | let day: i32 = 0 20 | 21 | def display() { 22 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 23 | glPushMatrix() 24 | 25 | glColor3f(1.0, 1.0, 0.0) 26 | myWireSphere(1.0, 15, 15) 27 | 28 | glRotatef(year as f32, 0.0, 1.0, 0.0) 29 | glTranslatef (2.0, 0.0, 0.0) 30 | glRotatef(day as f32, 0.0, 1.0, 0.0) 31 | glColor3f(0.0, 0.0, 1.0) 32 | myWireSphere(0.2, 15, 15) 33 | glColor3f(1.0, 1.0, 1.0) 34 | 35 | glBegin(GL_LINES) 36 | glVertex3f(0.0, -0.3, 0.0) 37 | glVertex3f(0.0, 0.3, 0.0) 38 | glEnd() 39 | 40 | glPopMatrix() 41 | glFlush() 42 | glutSwapBuffers() 43 | } 44 | 45 | let u: f32 = 0.0 46 | let du: f32 = 0.1 47 | 48 | def timer(v: i32) { 49 | u += du 50 | day = (day + 1) 51 | if day > 359 { 52 | day = 0 53 | } 54 | year = (year + 2) 55 | if year > 359 { 56 | year -= 360 57 | } 58 | glLoadIdentity() 59 | gluLookAt( 60 | 20.0 * cos(u/8.0) + 12.0, 61 | 5.0 * sin(u/8.0) + 1.0, 62 | 10.0 * cos(u/8.0) + 2.0, 63 | 0.0,0.0,0.0, 64 | 0.0,1.0,0.0 65 | ) 66 | glutPostRedisplay() 67 | glutTimerFunc(1000/60, timer, v) 68 | } 69 | 70 | def reshape(w: i32, h: i32) { 71 | glViewport(0, 0, w, h) 72 | glMatrixMode(GL_PROJECTION) 73 | glLoadIdentity() 74 | gluPerspective(60.0, w as f32 / h as f32, 1.0, 40.0) 75 | glMatrixMode(GL_MODELVIEW) 76 | } 77 | 78 | 79 | def main(argc: i32, argv: &string) { 80 | glutInit(&argc, argv); 81 | glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) 82 | glutInitWindowSize(800, 600) 83 | glutCreateWindow("On a Comet") 84 | glutDisplayFunc(display) 85 | glutReshapeFunc(reshape) 86 | glutTimerFunc(100, timer, 0) 87 | glEnable(GL_DEPTH_TEST) 88 | glutMainLoop() 89 | } -------------------------------------------------------------------------------- /examples/linked_list.ae: -------------------------------------------------------------------------------- 1 | /// out: "3 -> 4 -> 2 -> 1 -> end" 2 | 3 | struct Node { 4 | val: i32 5 | next: &Node 6 | } 7 | 8 | def Node::new(val: i32): &Node { 9 | let node = malloc(sizeof(Node)) as &Node; 10 | node.val = val; 11 | node.next = null; 12 | return node; 13 | } 14 | 15 | def Node::add_front(&this, val: i32): &Node { 16 | let node = Node::new(val); 17 | node.next = this; 18 | return node; 19 | } 20 | 21 | def Node::print_list(&this) { 22 | let node = this; 23 | while node? { 24 | print("%d -> ", node.val); 25 | node = node.next; 26 | } 27 | print("end\n"); 28 | } 29 | 30 | def main() { 31 | let head = Node::new(0) 32 | head = head.add_front(1) 33 | head = head.add_front(2) 34 | head = head.add_front(4) 35 | head = head.add_front(3) 36 | head.print_list(); 37 | } 38 | -------------------------------------------------------------------------------- /examples/raytrace.ae: -------------------------------------------------------------------------------- 1 | // Make raytracer go brrr 2 | @compiler c_flag "-O3" 3 | 4 | use "lib/vec.ae" 5 | use "lib/vector.ae" 6 | use "lib/image.ae" 7 | 8 | let TOL = 0.00001; 9 | 10 | def random_vec(): Vec { 11 | return Vec(rand01(), rand01(), rand01()).multf(2.0).subf(1.0); 12 | } 13 | 14 | def random_vec_unit(): Vec { 15 | while (true) { 16 | let vec = random_vec(); 17 | if vec.length_sq() < 1.0 { 18 | return vec.normalized() 19 | } 20 | } 21 | return Vec(0.0, 0.0, 0.0) 22 | } 23 | 24 | struct Ray { 25 | ori: Vec 26 | dir: Vec 27 | } 28 | 29 | def Ray::at(&this, t: f32): Vec { 30 | return .dir.multf(t).add(.ori); 31 | } 32 | 33 | struct Sphere { 34 | center: Vec 35 | color: Vec 36 | radius: f32 37 | } 38 | 39 | def Sphere::new(x: f32, y: f32, z: f32, radius: f32, r: f32, g: f32, b: f32): &Sphere { 40 | let s = malloc(sizeof(Sphere)) as &Sphere 41 | *s = Sphere(center: Vec(x, y, z), color: Vec(r, g, b), radius) 42 | return s 43 | } 44 | 45 | def Sphere::hit(&this, ray: &Ray, t: &f32, n: &Vec, col: &Vec): bool { 46 | let oc = ray.ori.sub(.center); 47 | let a = ray.dir.dot(ray.dir) 48 | let b = 2.0 * oc.dot(ray.dir) 49 | let c = oc.dot(oc) - .radius * .radius 50 | 51 | let disc = b * b - 4.0 * a * c 52 | if disc < 0.0 53 | return false 54 | let t0 = (-b - sqrt(disc)) / (2.0 * a) 55 | let t1 = (-b + sqrt(disc)) / (2.0 * a) 56 | 57 | let best = t0; 58 | if best < TOL 59 | best = t1 60 | if best < TOL 61 | return false 62 | 63 | *t = best 64 | *col = .color 65 | *n = ray.at(best).sub(.center).normalized() 66 | 67 | return true 68 | } 69 | 70 | def background_color(ray: &Ray): Vec { 71 | let t = 0.5 * (ray.dir.y + 1.0) 72 | let col2 = Vec(1.0, 1.0, 1.0).multf(1.0-t) 73 | 74 | let res = Vec(0.5, 0.7, 1.0) 75 | return res.multf(t).add(col2) 76 | } 77 | 78 | def find_hit(ray: &Ray, objs: &Vector, t: &f32, n: &Vec, obj_col: &Vec): i32 { 79 | let idx = -1 80 | 81 | for let i = 0; i < objs.size; i += 1 { 82 | let obj = objs.at(i) as &Sphere 83 | 84 | let tmp_t: f32 85 | let tmp_n: Vec 86 | let tmp_col: Vec 87 | 88 | if obj.hit(ray, &tmp_t, &tmp_n, &tmp_col) { 89 | if *t < 0.0 or tmp_t < *t { 90 | *t = tmp_t 91 | idx = i 92 | *obj_col = tmp_col 93 | *n = tmp_n 94 | } 95 | } 96 | } 97 | return idx 98 | } 99 | 100 | def raytrace(ray: &Ray, objs: &Vector, depth: i32): Vec { 101 | if depth < 0 102 | return Vec(0.0, 0.0, 0.0) 103 | 104 | let t = -1.0 105 | let n: Vec 106 | let obj_col: Vec 107 | 108 | if find_hit(ray, objs, &t, &n, &obj_col) < 0 109 | return background_color(ray) 110 | 111 | ray.ori = ray.at(t) 112 | ray.dir = random_vec_unit().add(n) 113 | 114 | let rec_col = raytrace(ray, objs, depth - 1) 115 | return rec_col.mult(obj_col) 116 | } 117 | 118 | def main() { 119 | let objs = Vector::new() 120 | objs.push(Sphere::new(0.0, 0.0, -1.0, 0.5, 1.0, 0.6, 0.3)) 121 | objs.push(Sphere::new(0.0, -100.5, -1.0, 100.0, 0.5, 0.5, 0.5)) 122 | 123 | // Image 124 | let aspect_ratio = 16.0 / 9.0 125 | let image_width = 800 126 | let image_height = (image_width as f32 / aspect_ratio) as i32 127 | let samples_per_pixel = 100 128 | 129 | // Camera 130 | let viewport_height = 2.0 131 | let viewport_width = aspect_ratio * viewport_height 132 | let focal_length = 1.0 133 | 134 | let origin = Vec(0.0, 0.0, 0.0) 135 | let horizontal = Vec(viewport_width, 0.0, 0.0) 136 | let vertical = Vec(0.0, viewport_height, 0.0) 137 | 138 | let ll_corner = (origin 139 | .sub(horizontal.divf(2.0)) 140 | .sub(vertical.divf(2.0)) 141 | .sub(Vec(0.0, 0.0, focal_length))) 142 | 143 | let img = Image::new(image_width, image_height); 144 | defer img.free() 145 | 146 | let div_factor = 1.0 / samples_per_pixel as f32; 147 | for let y = 0; y < image_height; y += 1 { 148 | print("\r%d / %d done", y, image_height) 149 | for let x = 0; x < image_width; x += 1 { 150 | 151 | let total_col = Vec(0.0, 0.0, 0.0) 152 | for let s = 0; s < samples_per_pixel; s += 1 { 153 | let u = (rand01() + x as f32) / (image_width-1) as f32 154 | let v = 1.0 - (rand01() + y as f32) / (image_height-1) as f32 155 | let ray: Ray 156 | ray.ori = origin 157 | ray.dir = (ll_corner 158 | .add(horizontal.multf(u)) 159 | .add(vertical.multf(v)) 160 | .sub(origin) 161 | .normalized()) 162 | let color = raytrace(&ray, objs, 5) 163 | total_col = total_col.add(color) 164 | } 165 | img.set(x, y, total_col.multf(div_factor)) 166 | } 167 | } 168 | println("") 169 | img.save("out.ppm"); 170 | } 171 | -------------------------------------------------------------------------------- /examples/sdl_raytrace.ae: -------------------------------------------------------------------------------- 1 | use "lib/sdl.ae" 2 | 3 | // Make raytracer go brrr 4 | @compiler c_flag "-O3" 5 | 6 | use "lib/vec.ae" 7 | use "lib/vector.ae" 8 | use "lib/image.ae" 9 | 10 | let TOL = 0.00001; 11 | 12 | def random_vec(): Vec { 13 | return Vec(rand01(), rand01(), rand01()).multf(2.0).subf(1.0); 14 | } 15 | 16 | def Vec::reflect(this, normal: Vec): Vec { 17 | return .sub(normal.multf(2.0 * .dot(normal))); 18 | } 19 | 20 | def random_vec_unit(): Vec { 21 | while (true) { 22 | let vec = random_vec(); 23 | if vec.length_sq() < 1.0 { 24 | return vec.normalized() 25 | } 26 | } 27 | return Vec(0.0, 0.0, 0.0) 28 | } 29 | 30 | struct Ray { 31 | ori: Vec 32 | dir: Vec 33 | } 34 | 35 | def Ray::at(&this, t: f32): Vec { 36 | return .dir.multf(t).add(.ori); 37 | } 38 | 39 | enum Material { 40 | Diffuse 41 | Mirror 42 | } 43 | 44 | struct Sphere { 45 | mat: Material 46 | center: Vec 47 | color: Vec 48 | radius: f32 49 | fuzz: f32 50 | } 51 | 52 | def Sphere::new(mat: Material, pos: Vec, radius: f32, color: Vec, fuzz: f32): &Sphere { 53 | let s = malloc(sizeof(Sphere)) as &Sphere 54 | *s = Sphere(mat, pos, color, radius, fuzz) 55 | return s 56 | } 57 | 58 | def Sphere::hit(&this, ray: &Ray, t: &f32, n: &Vec): bool { 59 | let oc = ray.ori.sub(.center); 60 | let a = ray.dir.dot(ray.dir) 61 | let b = 2.0 * oc.dot(ray.dir) 62 | let c = oc.dot(oc) - .radius * .radius 63 | 64 | let disc = b * b - 4.0 * a * c 65 | if disc < 0.0 66 | return false 67 | let t0 = (-b - sqrt(disc)) / (2.0 * a) 68 | let t1 = (-b + sqrt(disc)) / (2.0 * a) 69 | 70 | let best = t0; 71 | if best < TOL 72 | best = t1 73 | if best < TOL 74 | return false 75 | 76 | *t = best 77 | *n = ray.at(best).sub(.center).normalized() 78 | 79 | return true 80 | } 81 | 82 | def background_color(ray: &Ray): Vec { 83 | let t = 0.5 * (ray.dir.y + 1.0) 84 | let col2 = Vec(1.0, 1.0, 1.0).multf(1.0-t) 85 | 86 | let res = Vec(0.5, 0.7, 1.0) 87 | return res.multf(t).add(col2) 88 | } 89 | 90 | def find_hit(ray: &Ray, objs: &Vector, t: &f32, n: &Vec, hit_obj: &&Sphere): bool { 91 | let hit = false 92 | 93 | for let i = 0; i < objs.size; i += 1 { 94 | let obj = objs.at(i) as &Sphere 95 | 96 | let tmp_t: f32 97 | let tmp_n: Vec 98 | 99 | if obj.hit(ray, &tmp_t, &tmp_n) { 100 | if *t < 0.0 or tmp_t < *t { 101 | hit = true 102 | *t = tmp_t 103 | *hit_obj = obj 104 | *n = tmp_n 105 | } 106 | } 107 | } 108 | return hit 109 | } 110 | 111 | def raytrace(ray: &Ray, objs: &Vector, depth: i32): Vec { 112 | if depth < 0 113 | return Vec(0.0, 0.0, 0.0) 114 | 115 | let t = -1.0 116 | let n: Vec 117 | let obj: &Sphere 118 | 119 | if not find_hit(ray, objs, &t, &n, &obj) 120 | return background_color(ray) 121 | 122 | ray.ori = ray.at(t) 123 | match obj.mat { 124 | Diffuse => ray.dir = random_vec_unit().add(n).normalized().normalized() 125 | Mirror => ray.dir = ray.dir.reflect(n).add(random_vec_unit().multf(obj.fuzz)).normalized() 126 | } 127 | 128 | let rec_col = raytrace(ray, objs, depth - 1) 129 | return rec_col.mult(obj.color) 130 | } 131 | 132 | let WIDTH = 800 133 | let HEIGHT = 600 134 | 135 | let samples = 0 136 | let img: &Image 137 | let objs: &Vector 138 | 139 | struct Camera { 140 | origin: Vec 141 | direction: Vec 142 | 143 | horizontal: Vec 144 | vertical: Vec 145 | ll_corner: Vec 146 | 147 | u: Vec 148 | v: Vec 149 | w: Vec 150 | } 151 | 152 | def Camera::make(origin: Vec, direction: Vec): Camera { 153 | let cam: Camera 154 | let aspect_ratio = WIDTH as f32 / HEIGHT as f32 155 | 156 | let viewport_height = 2.0 157 | let viewport_width = aspect_ratio * viewport_height 158 | let focal_length = 1.0 159 | 160 | let up = Vec(0.0, 1.0, 0.0) 161 | 162 | cam.origin = origin 163 | cam.direction = direction.normalized() 164 | 165 | cam.w = cam.direction.multf(-1.0) 166 | cam.u = up.cross(cam.w).normalized() 167 | cam.v = cam.w.cross(cam.u) 168 | 169 | cam.horizontal = cam.u.multf(-viewport_width) 170 | cam.vertical = cam.v.multf(viewport_height) 171 | cam.ll_corner = (cam.origin 172 | .sub(cam.horizontal.divf(2.0)) 173 | .sub(cam.vertical.divf(2.0)) 174 | .sub(cam.w)) 175 | 176 | return cam 177 | } 178 | 179 | def Camera::ray(&this, x: i32, y: i32): Ray { 180 | let u = (rand01() + x as f32) / (WIDTH-1) as f32 181 | let v = 1.0 - (rand01() + y as f32) / (HEIGHT-1) as f32 182 | let ray: Ray 183 | ray.ori = .origin 184 | ray.dir = (.ll_corner 185 | .add(.horizontal.multf(u)) 186 | .add(.vertical.multf(v)) 187 | .sub(.origin) 188 | .normalized()) 189 | return ray 190 | } 191 | 192 | let camera: Camera 193 | 194 | def draw(screen: &u8, pitch: i32) { 195 | samples += 1 196 | for let x = 0; x < WIDTH; x += 1 { 197 | for let y = 0; y < HEIGHT; y += 1 { 198 | let ray = camera.ray(x, y) 199 | let color = raytrace(&ray, objs, 5) 200 | 201 | let prev_color = img.get(x, y) 202 | let new_color = prev_color.add(color) 203 | img.set(x, y, new_color) 204 | 205 | // Save to screen 206 | let r = (new_color.x / samples as f32) * 255.0 207 | let g = (new_color.y / samples as f32) * 255.0 208 | let b = (new_color.z / samples as f32) * 255.0 209 | 210 | let offset = (y * pitch) + (x * 4) 211 | screen[offset + 0] = r as u8 212 | screen[offset + 1] = g as u8 213 | screen[offset + 2] = b as u8 214 | screen[offset + 3] = 255 as u8 215 | } 216 | } 217 | } 218 | 219 | def Image::clear(&this) { 220 | set_memory(.data, 0, .width * .height * sizeof(Vec)) 221 | } 222 | 223 | def Vec::rotateX(&this, angle: f32): Vec { 224 | let c = cos(angle) 225 | let s = sin(angle) 226 | let y = .y * c - .z * s 227 | let z = .y * s + .z * c 228 | return Vec(.x, y, z) 229 | } 230 | 231 | def Vec::rotateY(&this, angle: f32): Vec { 232 | let c = cos(angle) 233 | let s = sin(angle) 234 | let z = .z * c - .x * s 235 | let x = .z * s + .x * c 236 | return Vec(x, .y, z) 237 | } 238 | 239 | def Vec::rotateZ(&this, angle: f32): Vec { 240 | let c = cos(angle) 241 | let s = sin(angle) 242 | let x = .x * c - .y * s 243 | let y = .x * s + .y * c 244 | return Vec(x, y, .z) 245 | } 246 | 247 | let window: &SDLWindow 248 | let renderer: &SDLRenderer 249 | 250 | def draw_text(font: &TTFFont, text: string, x: i32, y: i32) { 251 | let col = SDLColor(0, 0, 0, 255) 252 | let msg_surf = font.render_solid(text, col) 253 | let msg = renderer.create_texture_from_surface(msg_surf) 254 | let w = 0 255 | let h = 0 256 | msg.query(null, null, &w, &h) 257 | let rect = SDLRect(x, y, w, h) 258 | renderer.copy(msg, null, &rect) 259 | msg_surf.destroy() 260 | msg.destroy() 261 | } 262 | 263 | def main() { 264 | SDL::init(SDL_INIT_EVERYTHING) 265 | SDL::create_window_renderer(WIDTH, HEIGHT, 0, &window, &renderer) 266 | TTF::init() 267 | let font = TTFFont::load("lib/daecor/assets/font.ttf", 24) 268 | 269 | renderer.set_draw_color(0, 0, 0, 255) 270 | renderer.clear() 271 | renderer.present() 272 | 273 | let buffer = renderer.create_texture( 274 | SDL_PIXELFORMAT_ABGR8888, 275 | SDL_TEXTUREACCESS_STREAMING, 276 | WIDTH, 277 | HEIGHT 278 | ) 279 | 280 | objs = Vector::new() 281 | objs.push(Sphere::new(Material::Diffuse, Vec(0.0, 0.0, 1.0), 0.5, Vec(1.0, 0.6, 0.3), 0.0)) 282 | objs.push(Sphere::new(Material::Mirror, Vec(1.0, 0.0, 1.0), 0.5, Vec(0.8, 0.8, 0.8), 0.7)) 283 | objs.push(Sphere::new(Material::Mirror, Vec(-1.0, 0.0, 1.0), 0.5, Vec(0.8, 0.6, 0.2), 0.0)) 284 | objs.push(Sphere::new(Material::Diffuse, Vec(0.0, -100.5, 1.0), 100.0, Vec(0.5, 0.5, 0.5), 0.0)) 285 | 286 | 287 | let origin = Vec(0.0, 0.0, 0.0) 288 | let direction = Vec(0.0, 0.0, 1.0) 289 | camera = Camera::make(origin, direction) 290 | 291 | img = Image::new(WIDTH, HEIGHT); 292 | 293 | let e: SDLEvent 294 | 295 | let quit = false 296 | while not quit { 297 | let modified = false 298 | while SDL::poll_event(&e) { 299 | match e.type { 300 | Quit => quit = true 301 | KeyDown => { 302 | let cur_modified = true 303 | match e.key.keysym.sym { 304 | Q | Escape => quit = true 305 | W => origin = origin.add(camera.w.multf(-0.1)) 306 | S => origin = origin.add(camera.w.multf(0.1)) 307 | A => origin = origin.add(camera.u.multf(0.1)) 308 | D => origin = origin.add(camera.u.multf(-0.1)) 309 | Z => origin = origin.add(camera.v.multf(-0.1)) 310 | X => origin = origin.add(camera.v.multf(0.1)) 311 | 312 | Left => direction = direction.rotateY(-0.1) 313 | Right => direction = direction.rotateY(0.1) 314 | Up => direction = direction.rotateX(-0.1) 315 | Down => direction = direction.rotateX(0.1) 316 | 317 | R => { 318 | origin = Vec(0.0, 0.0, 0.0) 319 | direction = Vec(0.0, 0.0, 1.0) 320 | } 321 | else => cur_modified = false 322 | } 323 | modified = modified or cur_modified 324 | } 325 | else => {} 326 | } 327 | } 328 | 329 | if modified { 330 | camera = Camera::make(origin, direction) 331 | img.clear() 332 | samples = 0 333 | } 334 | 335 | let pixels: &u8 336 | let pitch = 0 337 | buffer.lock(null, (&pixels) as &untyped_ptr, &pitch) 338 | 339 | draw(pixels, pitch) 340 | 341 | buffer.unlock() 342 | renderer.copy(buffer, null, null) 343 | 344 | let text = `Samples: {samples}` 345 | draw_text(font, text, 10, 10) 346 | free(text) 347 | 348 | renderer.present() 349 | 350 | // SDL::delay(1000 / 60) 351 | } 352 | 353 | renderer.destroy() 354 | window.destroy() 355 | SDL::quit() 356 | 357 | return 0 358 | } 359 | -------------------------------------------------------------------------------- /find.ae: -------------------------------------------------------------------------------- 1 | use "compiler/lexer.ae" 2 | use "compiler/parser.ae" 3 | use "compiler/typecheck.ae" 4 | 5 | enum UsageType { 6 | Literal 7 | Variable 8 | Type 9 | Function 10 | } 11 | 12 | def UsageType::str(this): string => match this { 13 | Literal => "literal" 14 | Variable => "variable" 15 | Type => "type" 16 | Function => "function" 17 | } 18 | 19 | union UsageUnion { 20 | var: &Variable 21 | type: &Type 22 | func: &Function 23 | lit: &AST 24 | } 25 | 26 | struct Usage { 27 | type: UsageType 28 | u: UsageUnion 29 | } 30 | 31 | def Usage::span(&this): Span => match .type { 32 | Variable => .u.var.span 33 | Type => if .u.type.struct_def? { 34 | yield .u.type.struct_def.span 35 | } else { 36 | yield .u.type.span 37 | } 38 | Function => .u.func.span 39 | Literal => .u.lit.span 40 | } 41 | 42 | def Usage::etype(&this): &Type => match .type { 43 | Variable => .u.var.type 44 | Type => .u.type 45 | Function => .u.func.type 46 | Literal => .u.lit.etype 47 | } 48 | 49 | def Usage::new(type: UsageType): &Usage { 50 | let usage = calloc(1, sizeof(Usage)) as &Usage 51 | usage.type = type 52 | return usage 53 | } 54 | 55 | // Namespace thing again 56 | struct Finder { 57 | loc: Location // Location we are looking for 58 | usage: &Usage 59 | } 60 | 61 | def Finder::make(loc: Location): Finder { 62 | let finder: Finder 63 | finder.loc = loc 64 | return finder 65 | } 66 | 67 | def Finder::find_in_identifier(&this, node: &AST): bool { 68 | let ident = &node.u.ident 69 | if node.span.contains_loc(.loc) { 70 | if ident.var? { 71 | .usage = Usage::new(UsageType::Variable) 72 | .usage.u.var = ident.var 73 | } else if ident.func? { 74 | .usage = Usage::new(UsageType::Function) 75 | .usage.u.func = ident.func 76 | } 77 | return true 78 | } 79 | return false 80 | } 81 | 82 | def Finder::find_in_var(&this, var: &Variable): bool { 83 | if var.span.contains_loc(.loc) { 84 | .usage = Usage::new(UsageType::Variable) 85 | .usage.u.var = var 86 | return true 87 | } 88 | // TODO: check type 89 | return false 90 | } 91 | 92 | def Finder::find_in_literal(&this, node: &AST): bool { 93 | if node.span.contains_loc(.loc) { 94 | .usage = Usage::new(UsageType::Literal) 95 | .usage.u.lit = node 96 | return true 97 | } 98 | // TODO: check type 99 | return false 100 | } 101 | 102 | def Finder::find_in_call_args(&this, args: &Vector): bool { 103 | for let i = 0; i < args.size; i += 1 { 104 | let arg = args.at(i) as &Argument 105 | if arg.label? and .find_in_identifier(arg.label) return true 106 | if .find_in_expression(arg.expr) return true 107 | } 108 | return false 109 | } 110 | 111 | def Finder::find_in_expression(&this, node: &AST): bool { 112 | match node.type { 113 | IntLiteral | FloatLiteral | BoolLiteral | 114 | StringLiteral | CharLiteral | 115 | Null => return .find_in_literal(node) 116 | 117 | FormatStringLiteral => { 118 | let fmt = &node.u.fmt_str 119 | for let i = 0; i < fmt.exprs.size; i += 1 { 120 | if .find_in_expression(fmt.exprs.at(i)) return true 121 | } 122 | return .find_in_literal(node) 123 | } 124 | 125 | Identifier => return this.find_in_identifier(node) 126 | Member => { 127 | if .find_in_expression(node.u.member.lhs) return true 128 | if .find_in_identifier(node.u.member.rhs) return true 129 | } 130 | ScopeLookup => { 131 | // We actually want to point to the type, not the variable 132 | if .find_in_identifier(node.u.member.lhs) { 133 | .usage.type = UsageType::Type 134 | .usage.u.type = node.u.member.lhs.etype 135 | return true 136 | } 137 | if .find_in_identifier(node.u.member.rhs) return true 138 | } 139 | EnumValue => { 140 | // FIXME: Actually set the type in LHS, not identifier 141 | let val = &node.u.enum_val 142 | if val.lhs? and .find_in_expression(val.lhs) return true 143 | if .find_in_identifier(val.rhs) return true 144 | } 145 | And | Assignment | BitwiseAnd | 146 | BitwiseOr | BitwiseXor | Divide | DivideEquals | 147 | Equals | GreaterThan | GreaterThanEquals | Index | 148 | LeftShift | LessThan | LessThanEquals | Minus | 149 | MinusEquals | Modulus | Multiply | MultiplyEquals | 150 | NotEquals | Or | Plus | PlusEquals | 151 | RightShift => { 152 | let lhs = node.u.binary.lhs 153 | let rhs = node.u.binary.rhs 154 | return .find_in_expression(lhs) or .find_in_expression(rhs) 155 | } 156 | Address | Dereference | Not | UnaryMinus | 157 | Defer | IsNotNull | Yield => return .find_in_expression(node.u.unary) 158 | Call => { 159 | let call = &node.u.call 160 | if .find_in_expression(call.callee) return true 161 | if .find_in_call_args(call.args) return true 162 | } 163 | Constructor => { 164 | let cons = &node.u.constructor 165 | if .find_in_expression(cons.callee) return true 166 | if .find_in_call_args(cons.args) return true 167 | } 168 | Cast => { 169 | if .find_in_expression(node.u.cast.lhs) return true 170 | return .find_in_type(node.u.cast.to) 171 | } 172 | SizeOf => return .find_in_type(node.u.size_of_type) 173 | If => { 174 | let stmt = &node.u.if_stmt 175 | if .find_in_expression(stmt.cond) return true 176 | if .find_in_statement(stmt.body) return true 177 | if stmt.els? and .find_in_statement(stmt.els) return true 178 | } 179 | Match => { 180 | let stmt = &node.u.match_stmt 181 | if .find_in_expression(stmt.expr) return true 182 | for let i = 0; i < stmt.cases.size; i += 1 { 183 | let case_ = stmt.cases.at(i) as &MatchCase 184 | if .find_in_expression(case_.cond) return true 185 | if case_.body? and .find_in_statement(case_.body) return true 186 | } 187 | if stmt.defolt? and .find_in_statement(stmt.defolt) return true 188 | } 189 | Break | Continue => {} 190 | else => { 191 | println("Unhandled node type in Finder::find_in_expression: %s", node.type.str()) 192 | } 193 | } 194 | return false 195 | } 196 | 197 | def Finder::find_in_statement(&this, node: &AST): bool { 198 | match node.type { 199 | While | For => { 200 | let loop = &node.u.loop 201 | if loop.init? and .find_in_statement(loop.init) return true 202 | if loop.cond? and .find_in_expression(loop.cond) return true 203 | if loop.incr? and .find_in_expression(loop.incr) return true 204 | if loop.body? and .find_in_statement(loop.body) return true 205 | } 206 | VarDeclaration => { 207 | let decl = &node.u.var_decl 208 | if decl.var? and .find_in_var(decl.var) return true 209 | if decl.init? and .find_in_expression(decl.init) return true 210 | } 211 | Block => return .find_in_block(node) 212 | Return => return node.u.unary? and .find_in_expression(node.u.unary) 213 | else => return .find_in_expression(node) 214 | } 215 | return false 216 | } 217 | 218 | def Finder::find_in_block(&this, node: &AST): bool { 219 | let stmts = node.u.block.statements 220 | for let i = 0; i < stmts.size; i += 1 { 221 | if .find_in_statement(stmts.at(i)) return true 222 | } 223 | return false 224 | } 225 | 226 | def Finder::find_in_type(&this, type: &Type): bool { 227 | // FIXME: might want to only do this for base type... 228 | if type.span.contains_loc(.loc) { 229 | .usage = Usage::new(UsageType::Type) 230 | .usage.u.type = type 231 | return true 232 | } 233 | return false 234 | } 235 | 236 | def Finder::find_in_function(&this, func: &Function): bool { 237 | if func.span.contains_loc(.loc) { 238 | .usage = Usage::new(UsageType::Function) 239 | .usage.u.func = func 240 | return true 241 | } 242 | 243 | let params = func.type.params 244 | for let i = 0; i < params.size; i += 1 { 245 | let param = params.at(i) as &Variable 246 | if .find_in_var(param) return true 247 | if .find_in_type(param.type) return true 248 | } 249 | 250 | let ret_type = func.type.return_type 251 | if ret_type? and .find_in_type(ret_type) return true 252 | 253 | return func.body? and .find_in_statement(func.body) 254 | } 255 | 256 | def Finder::find_in_program(&this, program: &Program): bool { 257 | for let i = 0; i < program.structures.size; i += 1 { 258 | let struc = program.structures.at(i) as &Structure 259 | for let j = 0; j < struc.fields.size; j += 1 { 260 | let field = struc.fields.at(j) as &Variable 261 | if .find_in_var(field) return true 262 | if field.type? and .find_in_type(field.type) return true 263 | } 264 | } 265 | 266 | for let i = 0; i < program.functions.size; i += 1 { 267 | if .find_in_function(program.functions.at(i)) return true 268 | } 269 | 270 | return false 271 | } 272 | 273 | def Finder::find(&this, program: &Program): &Usage { 274 | if .find_in_program(program) { 275 | return .usage 276 | } 277 | return null 278 | } 279 | 280 | def string::to_i32(this): i32 extern("atoi") 281 | 282 | def main(argc: i32, argv: &string) { 283 | if argc < 4 { 284 | panic(`Usage: {argv[0]} `) 285 | } 286 | let filename = argv[1] 287 | 288 | let parser = Parser::new(filename) 289 | 290 | let program = Program::new() 291 | parser.include_prelude(program) 292 | j parser.include_file(program, filename) 293 | 294 | let checker = TypeChecker::new() 295 | checker.check_program(program) 296 | 297 | println("type checked program successfully") 298 | 299 | let loc = Location(filename, argv[2].to_i32(), argv[3].to_i32()) 300 | let finder = Finder::make(loc) 301 | 302 | let usage = finder.find(program) 303 | if usage? { 304 | println("Found usage:") 305 | println(" - type: %s", usage.etype().str()) 306 | println(" - loc: %s", usage.span().start.str()) 307 | } else { 308 | println("didn't find it") 309 | } 310 | } -------------------------------------------------------------------------------- /lib/bencode.ae: -------------------------------------------------------------------------------- 1 | // byte-encoding of objects used in BitTorrent protocol 2 | 3 | use "lib/value.ae" 4 | use "lib/buffer.ae" 5 | use "lib/span.ae" 6 | 7 | struct BencodeParser { 8 | input: string 9 | loc: Location 10 | } 11 | 12 | def BencodeParser::new(input: string): BencodeParser { 13 | return BencodeParser( 14 | input: input, 15 | loc: Location( 16 | filename: "", 17 | line: 1, 18 | col: 1, 19 | index: 0, 20 | ) 21 | ) 22 | } 23 | 24 | def BencodeParser::cur(&this): char { 25 | return .input[.loc.index] 26 | } 27 | 28 | def BencodeParser::parse(&this): &Value { 29 | let start = .loc 30 | let val = match .cur() { 31 | 'i' => .parse_int() 32 | 'l' => .parse_list() 33 | 'd' => .parse_dict() 34 | else => .parse_string() 35 | } 36 | val.span = Span(start, .loc) 37 | return val 38 | } 39 | 40 | def BencodeParser::inc(&this) { 41 | .loc.index += 1 42 | } 43 | 44 | def BencodeParser::parse_int_internal(&this): i64 { 45 | let scale = 1i64 46 | if .cur() == '-' { 47 | scale = -1 48 | .inc() 49 | } 50 | let num = 0i64 51 | while is_digit(.cur()) { 52 | num *= 10 53 | num += .cur() as i64 - '0' as i64 54 | .inc() 55 | } 56 | return num * scale 57 | } 58 | 59 | def BencodeParser::parse_list(&this): &Value { 60 | let val = Value::new(List) 61 | val.u.as_list = Vector::new() 62 | .inc() // skip 'l' 63 | while .cur() != 'e' { 64 | val.u.as_list.push(.parse()) 65 | } 66 | .inc() // skip 'e' 67 | return val 68 | } 69 | 70 | def BencodeParser::parse_dict(&this): &Value { 71 | let val = Value::new(Dictionary) 72 | val.u.as_dict = Map::new() 73 | .inc() // skip 'l' 74 | while .cur() != 'e' { 75 | let key = .parse_string_internal() 76 | let value = .parse() 77 | val.u.as_dict.insert(key.str(), value) 78 | } 79 | .inc() // skip 'e' 80 | return val 81 | } 82 | 83 | def BencodeParser::parse_string_internal(&this): Buffer { 84 | let len = .parse_int_internal() 85 | .inc() // skip ':' 86 | let s = .input.substring(.loc.index as i32, len as i32) 87 | 88 | for let i = 0i64; i < len; i += 1 { 89 | .inc() 90 | } 91 | return Buffer( 92 | data: s as &u8, 93 | size: len as i32, 94 | capacity: len as i32 95 | ) 96 | } 97 | 98 | def BencodeParser::parse_string(&this): &Value { 99 | let val = Value::new(String) 100 | val.u.as_str = .parse_string_internal() 101 | return val 102 | } 103 | 104 | def BencodeParser::parse_int(&this): &Value { 105 | let val = Value::new(Integer) 106 | .inc() // skip 'i' 107 | let num = .parse_int_internal() 108 | .inc() // skip 'e' 109 | val.u.as_num = num 110 | return val 111 | } 112 | 113 | // Namespace 114 | struct Bencode {} 115 | 116 | def Bencode::parse(s: string): &Value { 117 | let parser = BencodeParser::new(input: s) 118 | return parser.parse() 119 | } 120 | 121 | def Bencode::serialize_into(val: &Value, sb: &Buffer) { 122 | match val.type { 123 | String => { 124 | let str = val.u.as_str 125 | sb.putsf(`{str.size}:`) 126 | sb.putb(&str) 127 | } 128 | Integer => sb.putsf(`i{val.u.as_num}e`) 129 | List => { 130 | sb.puts("l") 131 | let lst = val.u.as_list 132 | for let i = 0; i < lst.size; i += 1 { 133 | let item = lst.at(i) as &Value 134 | Bencode::serialize_into(item, sb) 135 | } 136 | sb.puts("e") 137 | } 138 | Dictionary => { 139 | sb.puts("d") 140 | let map = val.u.as_dict 141 | for let iter = map.iter(); iter.cur?; iter.next() { 142 | let key = iter.cur.key as string 143 | let value = iter.cur.value as &Value 144 | sb.putsf(`{key.len()}:{key}`) 145 | Bencode::serialize_into(value, sb) 146 | } 147 | sb.puts("e") 148 | } 149 | else => panic(`Unsupported Value type {val.type} in Bencode::serialize_into()`) 150 | } 151 | } 152 | 153 | def Bencode::serialize(val: &Value): Buffer { 154 | let sb = Buffer::make() 155 | Bencode::serialize_into(val, &sb) 156 | return sb 157 | } -------------------------------------------------------------------------------- /lib/buffer.ae: -------------------------------------------------------------------------------- 1 | 2 | // Maybe abstract away the resizing with a `Buffer` type, 3 | // and share this with `Vector` 4 | struct Buffer { 5 | data: &u8 6 | size: i32 7 | capacity: i32 8 | } 9 | 10 | def Buffer::make(): Buffer { 11 | let initial_capacity = 16; 12 | return Buffer( 13 | data: calloc(initial_capacity, 1), 14 | size: 0, 15 | capacity: initial_capacity 16 | ) 17 | } 18 | 19 | def Buffer::from_string(s: string): Buffer { 20 | return Buffer( 21 | data: s as &u8, 22 | size: s.len(), 23 | capacity: s.len(), 24 | ) 25 | } 26 | 27 | def Buffer::from_sized_string(s: string, size: i32): Buffer { 28 | return Buffer( 29 | data: s as &u8, 30 | size: size as i32, 31 | capacity: s.len(), 32 | ) 33 | } 34 | 35 | def Buffer::resize_if_necessary(&this, new_size: i32) { 36 | if new_size >= .capacity { 37 | let new_capacity = max(.capacity * 2, new_size) 38 | .data = realloc(.data, new_capacity) as &u8 39 | if .data == null { 40 | println("Out of memory!") 41 | exit(1) 42 | } 43 | } 44 | } 45 | 46 | def Buffer::hex_dump(&this) { 47 | print("(%d bytes): ", .size) 48 | for let i = 0; i < .size; i += 1 { 49 | if (i % 4 == 0 and i > 0) print("_") 50 | print("%02x", .data[i]) 51 | } 52 | println("") 53 | } 54 | 55 | def Buffer::putb(&this, buf: &Buffer) { 56 | .resize_if_necessary(new_size: .size + buf.size + 1) 57 | copy_memory(.data + .size, buf.data, buf.size) 58 | .data[.size + buf.size] = '\0' as u8 59 | .size += buf.size 60 | } 61 | 62 | def Buffer::putbf(&this, buf: &Buffer) { 63 | .putb(buf) 64 | buf.free() 65 | } 66 | 67 | def Buffer::puts(&this, s: string) { 68 | let len = s.len() 69 | .resize_if_necessary(new_size: .size + len + 1) // +1 for null terminator 70 | copy_memory(.data + .size, s, len + 1) // Copy over null terminator 71 | .size += len 72 | } 73 | 74 | def Buffer::putc(&this, c: char) { 75 | .resize_if_necessary(new_size: .size + 2) // +1 for null terminator 76 | .data[.size] = c as u8 77 | .size += 1 78 | .data[.size] = '\0' as u8 79 | } 80 | 81 | // Put and free the string 82 | def Buffer::putsf(&this, s: string) { 83 | .puts(s) 84 | free(s) 85 | } 86 | 87 | def Buffer::str(&this): string => .data as string 88 | def Buffer::new_str(&this): string => (.data as string).copy() 89 | 90 | def Buffer::free(&this) { 91 | free(.data) 92 | } -------------------------------------------------------------------------------- /lib/bufferio.ae: -------------------------------------------------------------------------------- 1 | use "lib/buffer.ae" 2 | 3 | struct BufferIO { 4 | data: &Buffer 5 | index: i32 6 | } 7 | 8 | def BufferIO::make(data: &Buffer): BufferIO { 9 | return BufferIO( 10 | data: data, 11 | index: 0, 12 | ) 13 | } 14 | 15 | def BufferIO::read_i64_be(&this): i64 => .read_u64_be() as i64 16 | def BufferIO::read_u64_be(&this): u64 { 17 | let value = 0u64 18 | for let i = 0; i < 8; i += 1 { 19 | value = value | .data.data[.index + i] as u64 << (56u64 - (i as u64) * 8) 20 | } 21 | .index += 8 22 | return value as u64 23 | } 24 | 25 | 26 | def BufferIO::read_i32_be(&this): i32 => .read_u32_be() as i32 27 | def BufferIO::read_u32_be(&this): u32 { 28 | let value = 0u32 29 | for let i = 0; i < 4; i += 1 { 30 | value = value | .data.data[.index + i] as u32 << (24u32 - (i as u32) * 8) 31 | } 32 | .index += 4 33 | return value 34 | } 35 | 36 | def BufferIO::read_bytes(&this, _dst: untyped_ptr, count: i32) { 37 | let dst = _dst as &u8 38 | for let i = 0; i < count; i += 1 { 39 | dst[i] = .data.data[.index + i] 40 | } 41 | .index += count 42 | } 43 | 44 | def BufferIO::read_i16_be(&this): i16 => .read_i16_be() as i16 45 | def BufferIO::read_u16_be(&this): u16 { 46 | let value = 0u16 47 | for let i = 0u16; i < 2; i += 1 { 48 | value = value | .data.data[.index + i as i32] as u16 << (8u16 - i * 8) 49 | } 50 | .index += 2 51 | return value as u16 52 | } 53 | 54 | def BufferIO::write_i64_be(&this, value: i64) => .write_u64_be(value as u64) 55 | def BufferIO::write_u64_be(&this, value: u64) { 56 | .data.resize_if_necessary(.data.size + 8) 57 | for let i = 0u64; i < 8; i += 1 { 58 | .data.data[.data.size + i as i32] = ((value >> (56u64 - i * 8)) & 0xff) as u8 59 | } 60 | .data.size += 8 61 | } 62 | 63 | def BufferIO::write_i32_be(&this, value: i32) => .write_u32_be(value as u32) 64 | def BufferIO::write_u32_be(&this, value: u32) { 65 | .data.resize_if_necessary(.data.size + 4) 66 | for let i = 0u32; i < 4; i += 1 { 67 | .data.data[.data.size + i as i32] = ((value >> (24u32 - i * 8)) & 0xff) as u8 68 | } 69 | .data.size += 4 70 | } 71 | 72 | def BufferIO::write_i16_be(&this, value: i16) => .write_u16_be(value as u16) 73 | def BufferIO::write_u16_be(&this, value: u16) { 74 | .data.resize_if_necessary(.data.size + 2) 75 | for let i = 0u16; i < 2; i += 1 { 76 | .data.data[.data.size + i as i32] = ((value >> (8u16 - i * 8)) & 0xff) as u8 77 | } 78 | .data.size += 2 79 | } 80 | 81 | def BufferIO::write_i8(&this, value: i8) => .write_u8(value as u8) 82 | def BufferIO::write_u8(&this, value: u8) { 83 | .data.resize_if_necessary(.data.size + 1) 84 | .data.data[.data.size] = value 85 | .data.size += 1 86 | } 87 | 88 | def BufferIO::write_bytes(&this, _src: untyped_ptr, count: i32) { 89 | let src = _src as &u8 90 | .data.resize_if_necessary(.data.size + count) 91 | for let i = 0; i < count; i += 1 { 92 | .data.data[.data.size + i] = src[i] 93 | } 94 | .data.size += count 95 | } 96 | -------------------------------------------------------------------------------- /lib/daecor/all.ae: -------------------------------------------------------------------------------- 1 | use "lib/daecor/sdl.ae" 2 | use "lib/daecor/vec.ae" 3 | use "lib/daecor/colors.ae" 4 | use "lib/daecor/types.ae" 5 | use "lib/daecor/graphics.ae" 6 | use "lib/daecor/input.ae" 7 | use "lib/daecor/events.ae" 8 | use "lib/daecor/text.ae" 9 | use "lib/daecor/image.ae" 10 | use "lib/daecor/sound.ae" 11 | 12 | 13 | def init() { 14 | gfx_init() 15 | input_init() 16 | text_init() 17 | sound_init() 18 | } 19 | 20 | -------------------------------------------------------------------------------- /lib/daecor/assets/FFL.txt: -------------------------------------------------------------------------------- 1 | Fontshare EULA 2 | 3 | 4 | ---—---------------------------------—------------------------------ 5 | Free Font - End User License Agreement (FF EULA) 6 | ---—---------------------------------—------------------------------ 7 | Notice to User 8 | Indian Type Foundry designs, produces and distributes font software as digital fonts to end users worldwide. In addition to commercial fonts that are available for a fee, ITF also offers several fonts which can be used free of charge. The free fonts are distributed through a dedicated platform called www.fontshare.com (“Fontshare”) to end users worldwide. These free fonts are subject to this legally binding EULA between the Indian Type Foundry (“Indian Type Foundry” or “Licensor”) and you (“Licensee”).  9 | You acknowledge that the Font Software and designs embodied therein are protected by the copyright, other intellectual property rights and industrial property rights and by international treaties. They are and remain at all times the intellectual property of the Indian Type Foundry. 10 | In addition to direct download, Fontshare also offers these free fonts via Fonthsare API using a code. In this case, the Font Software is delivered directly from the servers used by Indian Type Foundry to the Licensee's website, without the Licensee having to download the Font Software. 11 | By downloading, accessing the API, installing, storing, copying or using one of any Font Software, you agree to the following terms.  12 | 13 | Definitions 14 | “Font Software” refers to the set of computer files or programs released under this license that instructs your computer to display and/or print each letters, characters, typographic designs, ornament and so forth. Font Software includes all bitmap and vector representations of fonts and typographic representations and embellishments created by or derived from the Font Software.  15 | “Original Version” refers to the Font Software as distributed by the Indian Type Foundry as the copyright holder.  16 | “Derivative Work” refers to the pictorial representation of the font created by the Font Software, including typographic characters such as letters, numerals, ornaments, symbols, or punctuation and special characters. 17 | 18 | 01. Grant of License 19 | You are hereby granted a non-exclusive, non-assignable, non-transferrable, terminable license to access, download and use the Font Software for your personal or commercial use for an unlimited period of time for free of charge.  20 | You may use the font Software in any media (including Print, Web, Mobile, Digital, Apps, ePub, Broadcasting and OEM) at any scale, at any location worldwide.  21 | You may use the Font Software to create logos and other graphic elements, images on any surface, vector files or other scalable drawings and static images.  22 | You may use the Font Software on any number of devices (computer, tablet, phone). The number of output devices (Printers) is not restricted.  23 | You may make only such reasonable number of back-up copies suitable to your permitted use.  24 | You may but are not required to identify Indian Type Foundry Fonts in your work credits.  25 | 26 | 02. Limitations of usage 27 | You may not modify, edit, adapt, translate, reverse engineer, decompile or disassemble, alter or otherwise copy the Font Software or the designs embodied therein in whole or in part, without the prior written consent of the Licensor.  28 | The Fonts may not - beyond the permitted copies and the uses defined herein - be distributed, duplicated, loaned, resold or licensed in any way, whether by lending, donating or give otherwise to a person or entity. This includes the distribution of the Fonts by e-mail, on USB sticks, CD-ROMs, or other media, uploading them in a public server or making the fonts available on peer-to-peer networks. A passing on to external designers or service providers (design agencies, repro studios, printers, etc.) is also not permitted.  29 | You are not allowed to transmit the Font Software over the Internet in font serving or for font replacement by means of technologies such as but not limited to EOT, Cufon, sIFR or similar technologies that may be developed in the future without the prior written consent of the Licensor.  30 | 31 | 03. Embedding 32 | You may embed the Font Software in PDF and other digital documents provided that is done in a secured, read-only mode. It must be ensured beyond doubt that the recipient cannot use the Font Software to edit or to create new documents. The design data (PDFs) created in this way and under these created design data (PDFs) may be distributed in any number.  33 | The extraction of the Font Software in whole or in part is prohibited.  34 | 35 | 04. Third party use, Commercial print service provider 36 | You may include the Font Software in a non-editable electronic document solely for printing and display purposes and provide that electronic document to the commercial print service provider for the purpose of printing. If the print service needs to install the fonts, they too need to download the Font Software from the Licensor's website. 37 | 38 | 05. Derivative Work 39 | You are allowed to make derivative works as far as you use them for your personal or commercial use. However, you cannot modify, make changes or reverse engineer the original font software provided to you. Any derivative works are the exclusive property of the Licensor and shall be subject to the terms and conditions of this EULA. Derivative works may not be sub-licensed, sold, leased, rented, loaned, or given away without the express written permission of the Licensor.  40 | 41 | 06. Warranty and Liability 42 | BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, INDIAN TYPE FOUNDRY MAKES NO WARRANTIES, EXPRESS OR IMPLIED AS TO THE MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR OTHERWISE. THE FONT SOFTWARE WAS NOT MANUFACTURED FOR USE IN MANUFACTURING CONTROL DEVICES OR NAVIGATION DEVICES OR IN CIRCUMSTANCES THAT COULD RESULT IN ENVIRONMENTAL DAMAGE OR PERSONAL INJURY. WITHOUT LIMITING THE FOREGOING, INDIAN TYPE FOUNDRY SHALL IN NO EVENT BE LIABLE TO THE LICENSED USER OR ANY OTHER THIRD PARTY FOR ANY DIRECT, CONSEQUENTIAL OR INCIDENTAL DAMAGES, INCLUDING DAMAGES FROM LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION NOR FOR LOST PROFITS OR SAVINGS ARISING OUT OF THE USE OR INABILITY TO USE THE PRODUCT EVEN IF NOTIFIED IN ADVANCE, UNDER NO CIRCUMSTANCES SHALL INDIAN TYPE FOUNDRY’S LIABILITY EXCEED THE REPLACEMENT COST OF THE SOFTWARE.  43 | IF LICENSEE CHOOSES TO ACCESS THE FONT SOFTWARE THROUGH A CODE (API), IT MAY HAVE A DIRECT IMPACT ON LICENSEE'S WEBSITE OR APPLICATIONS. INDIAN TYPE FOUNDRY IS NOT RESPONSIBLE OR LIABLE FOR ANY INTERRUPTION, MALFUNCTION, DOWNTIME OR OTHER FAILURE OF THE WEBSITE OR ITS API. 44 | 45 | 07. Updates, Maintenance and Support Services 46 | Licensor will not provide you with any support services for the Software under this Agreement. 47 | 48 | 08. Termination  49 | Any breach of the terms of this agreement shall be a cause for termination, provided that such breach is notified in writing to the Licensee by the Licensor and the Licensee failed to rectify the breach within 30 days of the receipt of such notification.  50 | In the event of termination and without limitation of any remedies under law or equity, you must delete the Font Software and all copies thereof. Proof of this must be provided upon request of the Licensor.   51 | We reserve the right to claim damages for the violation of the conditions.  52 | 53 | 09. Final Provisions 54 | If individual provisions of this agreement are or become invalid, the validity of the remaining provisions shall remain unaffected. Invalid provisions shall be replaced by mutual agreement by such provisions that are suitable to achieve the desired economic purpose, taking into account the interests of both parties. The same shall apply mutatis mutandis to the filling of any gaps which may arise in this agreement. 55 | This contract is subject to laws of the Republic of India. Place of performance and exclusive place of jurisdiction for all disputes between the parties arising out of or in connection with this contract is, as far as legally permissible, Ahmedabad, India. 56 | -  57 | Last Updated on 22 March 2021 58 | Copyright 2021 Indian Type Foundry. All rights reserved.  -------------------------------------------------------------------------------- /lib/daecor/assets/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mustafaquraish/aecor/f81543a34ee363dcc00e8632fd7cfcd4a3478b23/lib/daecor/assets/font.ttf -------------------------------------------------------------------------------- /lib/daecor/assets/pew.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mustafaquraish/aecor/f81543a34ee363dcc00e8632fd7cfcd4a3478b23/lib/daecor/assets/pew.wav -------------------------------------------------------------------------------- /lib/daecor/colors.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | use "lib/math.ae" 4 | 5 | def Color::rgb(r: i32, g: i32, b:i32): Color => Color(r, g, b, 255) 6 | 7 | def Color::rand(): Color { 8 | return Color::rgb( 9 | randint() % 255, 10 | randint() % 255, 11 | randint() % 255, 12 | ) 13 | } 14 | 15 | def Color::str(this): string => `rgb({this.r}, {this.g}, {this.b}, {this.a})` 16 | 17 | def Color::White(): Color => Color(0xFF, 0xFF, 0xFF, 0xFF) 18 | def Color::Pink(): Color => Color(0xFF, 0xC0, 0xCB, 0xFF) 19 | def Color::Cyan(): Color => Color(0x00, 0xFF, 0xFF, 0xFF) 20 | def Color::Red(): Color => Color(0xFF, 0x00, 0x00, 0xFF) 21 | def Color::Yellow(): Color => Color(0xFF, 0xFF, 0x00, 0xFF) 22 | def Color::Green(): Color => Color(0x00, 0xFF, 0x00, 0xFF) 23 | def Color::Blue(): Color => Color(0x00, 0x00, 0xFF, 0xFF) 24 | def Color::Magenta(): Color => Color(0xFF, 0x00, 0xFF, 0xFF) 25 | def Color::Black(): Color => Color(0x00, 0x00, 0x00, 0xFF) 26 | def Color::Grey(): Color => Color(0x80, 0x80, 0x80, 0xFF) 27 | 28 | def Color::Grey1(): Color => Color(0xAA, 0xAA, 0xAA, 0xFF) 29 | def Color::Grey2(): Color => Color(0x7E, 0x7E, 0x7E, 0xFF) 30 | def Color::Grey3(): Color => Color(0x52, 0x52, 0x52, 0xFF) 31 | 32 | def Color::to_u32(this): u32 { 33 | return ( 34 | .r << 0 | .g << 8 | .b << 16 | .a << 24 35 | ) as u32 36 | } 37 | 38 | def Color::from_u32(val: u32): Color { 39 | return Color( 40 | ((val as i32 >> 0) & 0xff) as i32, 41 | ((val as i32 >> 8) & 0xff) as i32, 42 | ((val as i32 >> 16) & 0xff) as i32, 43 | ((val as i32 >> 24) & 0xff) as i32, 44 | ) 45 | } -------------------------------------------------------------------------------- /lib/daecor/events.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | def handle_events(end_frame: i32) { 4 | let ticks_left = end_frame - get_ticks() 5 | mouse.prev = mouse.curr 6 | keys.prev = keys.curr 7 | 8 | while true { 9 | let event: Event 10 | let timeout = if ticks_left > 0 then ticks_left else 1 11 | 12 | if SDL::wait_event_timeout(&event, timeout) { 13 | match event.type { 14 | Quit => { 15 | println("Quitting now!") 16 | exit(0) 17 | } 18 | KeyDown => { 19 | // println("got keydown %d", event.key.keysym.sym) 20 | let k = event.key.keysym.scancode 21 | if k as i32 < 1024 { 22 | keys.curr.keys[k as i32] = true 23 | } 24 | } 25 | KeyUp => { 26 | // println("got keyup %d", event.key.keysym.sym) 27 | let k = event.key.keysym.scancode 28 | if k as i32 < 1024 { 29 | keys.curr.keys[k as i32] = false 30 | } 31 | } 32 | MouseDown => { 33 | // println("got mousebuttondown %s", event.button.button.str()) 34 | let b = event.button.button 35 | mouse.curr.buttons[b as i32] = true 36 | } 37 | MouseUp => { 38 | // println("got mousebuttonup %s", event.button.button.str()) 39 | let b = event.button.button 40 | mouse.curr.buttons[b as i32] = false 41 | } 42 | Wheel => { 43 | // println("got wheel %d %d", event.wheel.x, event.wheel.y) 44 | mouse.curr.wheel_dirs[MouseWheel::Right as i32] = event.wheel.x > 0 45 | mouse.curr.wheel_dirs[MouseWheel::Left as i32] = event.wheel.x < 0 46 | mouse.curr.wheel_dirs[MouseWheel::Down as i32] = event.wheel.y > 0 47 | mouse.curr.wheel_dirs[MouseWheel::Up as i32] = event.wheel.y < 0 48 | } 49 | Window => { 50 | gfx.prev_size = get_window_size() 51 | } 52 | else => {} 53 | } 54 | } 55 | ticks_left = end_frame - get_ticks(); 56 | if ticks_left <= 0 then break 57 | } 58 | SDL::get_mouse_state(&mouse.curr.pos.x, &mouse.curr.pos.y); 59 | SDL::get_relative_mouse_state(&mouse.curr.vec.x, &mouse.curr.vec.y); 60 | } -------------------------------------------------------------------------------- /lib/daecor/image.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | struct Image { 4 | tex: &Texture 5 | width: i32 6 | height: i32 7 | } 8 | 9 | def Image::destroy(&this) { 10 | .tex.destroy() 11 | free(this) 12 | } 13 | 14 | def Image::load(path: string): &Image { 15 | let tex = gfx.renderer.load_image(path) 16 | let w = 0 17 | let h = 0 18 | tex.query(null, null, &w, &h) 19 | 20 | let image = calloc(1, sizeof(Image)) as &Image 21 | *image = Image(tex, w, h) 22 | return image 23 | } 24 | 25 | def Image::draw(&this, pos: Vec2i) { 26 | let rect = Rect(pos.x, pos.y, .width, .height) 27 | gfx.renderer.copy(.tex, null, &rect) 28 | } 29 | 30 | def Image::draw_t(&this, pos: Vec2i, scale: f32, angle: f32) { 31 | let new_width = (.width as f32 * scale) as i32 32 | let new_height = (.height as f32 * scale) as i32 33 | let rect = Rect(pos.x, pos.y, new_width, new_height) 34 | 35 | gfx.renderer.copy_ex(.tex, null, &rect, degrees(angle), null, 0) 36 | } 37 | 38 | def Image::draw_centered(&this, pos: Vec2i) { 39 | let new_pos = Vec2i(pos.x - .width / 2, pos.y - .height / 2) 40 | .draw(new_pos) 41 | } 42 | 43 | def Image::draw_centered_t(&this, pos: Vec2i, scale: f32, angle: f32) { 44 | let new_width = (.width as f32 * scale) as i32 45 | let new_height = (.height as f32 * scale) as i32 46 | let rect = Rect( 47 | pos.x - new_width / 2, 48 | pos.y - new_height / 2, 49 | new_width, new_height) 50 | 51 | gfx.renderer.copy_ex(.tex, null, &rect, degrees(angle), null, 0) 52 | } 53 | 54 | def Image::draw_rect(&this, rect: Rect) { 55 | gfx.renderer.copy(.tex, null, &rect) 56 | } 57 | 58 | def Image::draw_rect_t(&this, rect: Rect, angle: f32) { 59 | gfx.renderer.copy_ex(.tex, null, &rect, degrees(angle), null, 0) 60 | } -------------------------------------------------------------------------------- /lib/daecor/input.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | // Making this a struct so copying curr->prev is easier 4 | struct KeyStateSingle { 5 | keys: [bool; SDL_NUM_SCANCODES] // Hopefully this is enough 6 | } 7 | 8 | struct KeyState { 9 | curr: KeyStateSingle, 10 | prev: KeyStateSingle 11 | } 12 | 13 | let keys: KeyState 14 | 15 | def KeyState::currs(&this, key: Key): bool => .curr.keys[key as i32] 16 | def KeyState::prevs(&this, key: Key): bool => .prev.keys[key as i32] 17 | 18 | def KeyState::state(&this, key: Key): bool => .currs(key) 19 | def KeyState::pressed(&this, key: Key): bool => .currs(key) and not .prevs(key) 20 | def KeyState::released(&this, key: Key): bool => not .currs(key) and .prevs(key) 21 | def KeyState::held(&this, key: Key): bool => .currs(key) and .prevs(key) 22 | def KeyState::changed(&this, key: Key): bool => .currs(key) != .prevs(key) 23 | 24 | enum MouseWheel { 25 | Down 26 | Right 27 | Up 28 | Left 29 | } 30 | 31 | struct MouseStateSingle { 32 | vec: Vec2i 33 | pos: Vec2i 34 | buttons: [bool; 16] // Hopefully enough for all mouse buttons 35 | wheel_dirs: [bool; 8] // Hopefully enough for all mouse wheel buttons 36 | } 37 | 38 | def MouseStateSingle::state(&this, which: MouseButton): bool => .buttons[which as i32] 39 | def MouseStateSingle::wheel(&this, which: MouseWheel): bool => .wheel_dirs[which as i32] 40 | 41 | struct MouseState { 42 | curr: MouseStateSingle 43 | prev: MouseStateSingle 44 | } 45 | 46 | let mouse: MouseState 47 | 48 | def MouseState::state(&this, button: MouseButton): bool => .curr.state(button) 49 | def MouseState::pressed(&this, button: MouseButton): bool => .curr.state(button) and not .prev.state(button) 50 | def MouseState::held(&this, button: MouseButton): bool => .curr.state(button) and .prev.state(button) 51 | def MouseState::released(&this, button: MouseButton): bool => not .curr.state(button) and .prev.state(button) 52 | def MouseState::changed(&this, button: MouseButton): bool => .curr.state(button) != .prev.state(button) 53 | 54 | def MouseState::scrolled(&this, wheel: MouseWheel): bool => .curr.wheel(wheel) 55 | def MouseState::moving(&this): bool => not .curr.vec.is_zero() and not .prev.vec.is_zero() 56 | def MouseState::move_start(&this): bool => not .curr.vec.is_zero() and .prev.vec.is_zero() 57 | def MouseState::move_stop(&this): bool => .curr.vec.is_zero() and not .prev.vec.is_zero() 58 | 59 | def MouseState::movement(&this): Vec2i => .curr.pos.sub(.prev.pos) 60 | def MouseState::pos(&this): Vec2i => .curr.pos 61 | 62 | def input_init() { 63 | set_memory(&keys, 0 as u8, sizeof(KeyState)) 64 | set_memory(&mouse, 0 as u8, sizeof(MouseState)) 65 | mouse.curr.pos = get_window_size().divi(2) 66 | mouse.prev.pos = mouse.curr.pos 67 | } 68 | -------------------------------------------------------------------------------- /lib/daecor/sound.ae: -------------------------------------------------------------------------------- 1 | 2 | 3 | const SOUND_NUM_CHANNELS = 32 4 | 5 | def sound_init() { 6 | let res = SDLMixer::open_audio(44100, MIX_DEFAULT_FORMAT, 2, 1024) 7 | if res != 0 { 8 | println(`Could not open audio: {SDLMixer::get_error()}`) 9 | exit(1) 10 | } 11 | SDLMixer::allocate_channels(SOUND_NUM_CHANNELS) 12 | } 13 | 14 | def set_sound_muted(muted: bool) { 15 | if muted { 16 | SDLMixer::pause(-1) 17 | } else { 18 | SDLMixer::resume(-1) 19 | } 20 | } 21 | 22 | def set_sound_volume(volume: i32) { 23 | SDLMixer::volume(-1, volume) 24 | } 25 | 26 | def set_channel_volume(channel: i32, volume: i32) { 27 | SDLMixer::volume(channel, volume) 28 | } 29 | 30 | def Sound::load(path: string): &Sound { 31 | let chunk = SDLMixer::load_wav(path) 32 | if chunk == null { 33 | println(`Could not load sound: {SDLMixer::get_error()}`) 34 | exit(1) 35 | } 36 | return chunk 37 | } 38 | 39 | def Sound::play(&this, channel: i32, times: i32) { 40 | let time = if times == -1 then -1 else times - 1 41 | SDLMixer::play_channel(channel, this, time) 42 | } 43 | 44 | def Sound::play_once(&this) { 45 | .play(channel: -1, times: 1) 46 | } 47 | -------------------------------------------------------------------------------- /lib/daecor/text.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | let font_file_path = "" 4 | 5 | def draw_text(text: string, pos: Vec2i): Rect { 6 | let rect = Rect(pos.x, pos.y, 0, 0) 7 | let msg_surf = gfx.font.render_solid(text, gfx.font_color) 8 | let msg = gfx.renderer.create_texture_from_surface(msg_surf) 9 | msg.query(null, null, &rect.w, &rect.h) 10 | gfx.renderer.copy(msg, null, &rect) 11 | msg_surf.destroy() 12 | msg.destroy() 13 | return rect 14 | } 15 | 16 | def draw_text_centered(text: string, pos: Vec2i): Rect { 17 | let rect = Rect(pos.x, pos.y, 0, 0) 18 | let msg_surf = gfx.font.render_solid(text, gfx.font_color) 19 | let msg = gfx.renderer.create_texture_from_surface(msg_surf) 20 | msg.query(null, null, &rect.w, &rect.h) 21 | rect.x -= rect.w / 2 22 | rect.y -= rect.h / 2 23 | gfx.renderer.copy(msg, null, &rect) 24 | msg_surf.destroy() 25 | msg.destroy() 26 | return rect 27 | } 28 | 29 | def get_size_for_text(text: string): Vec2i { 30 | let size: Vec2i 31 | gfx.font.get_text_size(text, &size.x, &size.y) 32 | return size 33 | } 34 | 35 | def set_font_size(size: i32) { 36 | if size == gfx.font_size then return 37 | if gfx.font? then gfx.font.close() 38 | 39 | gfx.font_size = size 40 | gfx.font = TTFFont::load(font_file_path, size) 41 | if not gfx.font? { 42 | panic(`Unable to open font or set font size! Error: {TTF::get_error()}`) 43 | } 44 | } 45 | 46 | def get_font_size(): i32 => gfx.font_size 47 | 48 | def set_text_color(color: Color) { 49 | gfx.font_color = color 50 | } 51 | 52 | def get_text_color(): Color => gfx.font_color 53 | 54 | def text_quit() { 55 | if gfx.font? then gfx.font.close() 56 | TTF::quit() 57 | } 58 | 59 | def text_init() { 60 | if TTF::init() < 0 { 61 | panic(`Could not initialize TTF: {TTF::get_error()}`) 62 | } 63 | 64 | font_file_path = get_environment_variable("DAECOR_FONT") 65 | if not font_file_path? or not File::exists(font_file_path) { 66 | font_file_path = "lib/daecor/assets/font.ttf" 67 | if not File::exists(font_file_path) { 68 | let aecor_root = get_environment_variable("AECOR_ROOT") 69 | let found = if not aecor_root? { 70 | yield false 71 | } else { 72 | font_file_path = `{aecor_root}/lib/daecor/assets/font.ttf` 73 | yield File::exists(font_file_path) 74 | } 75 | if not found { 76 | println("[-] Error: Could not find font file for Daecor") 77 | println(" [-] Hint: Set the AECOR_ROOT environment variable, or") 78 | println(" [-] Hint: Set the DAECOR_FONT environment variable to the path of your font file") 79 | panic("Exiting...") 80 | } 81 | } 82 | } 83 | 84 | gfx.font_color = Color::White() 85 | set_font_size(32) 86 | register_exit_callback(text_quit) 87 | } 88 | -------------------------------------------------------------------------------- /lib/daecor/types.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | 3 | struct Time extern("time_t") 4 | 5 | def register_exit_callback(func: fn()) extern("atexit") 6 | 7 | 8 | def Rect::from_points(p1: Vec2i, p2: Vec2i): Rect 9 | => Rect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y) 10 | 11 | def Rect::corner(this): Vec2i =>Vec2i(.x, .y) 12 | def Rect::size(this): Vec2i => Vec2i(.w, .h) 13 | 14 | 15 | enum Direction { 16 | Up 17 | Right 18 | Down 19 | Left 20 | } 21 | 22 | def Direction::to_char(this): char => match this { 23 | Up => 'U', 24 | Right => 'R', 25 | Down => 'D', 26 | Left => 'L' 27 | } 28 | 29 | def Direction::offset(this, amount: i32): Direction { 30 | return (((this as i32) + amount) % 4) as Direction 31 | } 32 | 33 | def Direction::inv(this): Direction => .offset(2) 34 | def Direction::rot_left(this): Direction => .offset(-1) 35 | def Direction::rot_right(this): Direction => .offset(1) 36 | 37 | def absf(a: f32): f32 extern("fabs") 38 | 39 | def abs(a: i32): i32 => if a < 0 then -a else a 40 | def sign(x: i32): i32 => if x < 0 then -1 else 1 41 | def signf(x: f32): f32 => if x < 0.0 then -1.0 else 1.0 42 | 43 | def match_sign(target: i32, val: i32): i32 { 44 | return if target < 0 then -val else val 45 | } 46 | 47 | def match_signf(target: f32, val: f32): f32 { 48 | return if target < 0.0 then -val else val 49 | } 50 | -------------------------------------------------------------------------------- /lib/daecor/vec.ae: -------------------------------------------------------------------------------- 1 | // Do not include this file directly, use lib/daecor/all.ae 2 | use "lib/math.ae" 3 | 4 | def modf(n: f32, m: f32): f32 extern("fmod") 5 | 6 | struct Vec2i { 7 | x: i32 8 | y: i32 9 | } 10 | 11 | struct Vec2iPair { 12 | v1: Vec2i 13 | v2: Vec2i 14 | } 15 | 16 | struct Vec2f { 17 | x: f32 18 | y: f32 19 | } 20 | 21 | def Vec2f::radians(this): f32 => atan2(.y, .x) 22 | def Vec2f::degrees(this): f32 => degrees(.radians()) 23 | def Vec2f::from_rad(rad: f32): Vec2f => Vec2f(cos(rad), sin(rad)) 24 | def Vec2f::from_polar(radians: f32, length: f32): Vec2f => Vec2f::from_rad(radians).multf(length) 25 | def Vec2f::from_polar_deg(deg: f32, length: f32): Vec2f => Vec2f::from_polar(radians(deg), length) 26 | def Vec2f::from_degrees(degrees: f32): Vec2f => Vec2f::from_rad(radians(degrees)) 27 | 28 | def Vec2f::to_i(this): Vec2i => Vec2i(.x as i32, .y as i32) 29 | def Vec2i::to_f(this): Vec2f => Vec2f(.x as f32, .y as f32) 30 | 31 | def Vec2f::min(this): f32 => minf(.x, .y) 32 | def Vec2f::max(this): f32 => maxf(.x, .y) 33 | 34 | def Vec2i::min(this): i32 => min(.x, .y) 35 | def Vec2i::max(this): i32 => max(.x, .y) 36 | 37 | def in_bounds(n: i32, min: i32, max: i32): bool => min <= n <= max 38 | def in_boundsf(n: f32, min: f32, max: f32): bool => min <= n <= max 39 | 40 | def wrapf(n: f32, min: f32, max: f32): f32 => min + modf(n - min, max - min) 41 | 42 | /// General vector overloads, Vec2i 43 | 44 | def Vec2i::is_zero(&this): bool => .x == 0 and .y == 0 45 | 46 | def Vec2i::add(this, other: Vec2i): Vec2i => Vec2i(.x + other.x, .y + other.y) 47 | def Vec2i::sub(this, other: Vec2i): Vec2i => Vec2i(.x - other.x, .y - other.y) 48 | def Vec2i::div(this, other: Vec2i): Vec2i => Vec2i(.x / other.x, .y / other.y) 49 | def Vec2i::mult(this, other: Vec2i): Vec2i => Vec2i(.x * other.x, .y * other.y) 50 | 51 | def Vec2i::addi(this, val: i32): Vec2i => Vec2i(.x + val, .y + val) 52 | def Vec2i::subi(this, val: i32): Vec2i => Vec2i(.x - val, .y - val) 53 | def Vec2i::divi(this, val: i32): Vec2i => Vec2i(.x / val, .y / val) 54 | def Vec2i::multi(this, val: i32): Vec2i => Vec2i(.x * val, .y * val) 55 | 56 | 57 | def Vec2i::neg(this): Vec2i => Vec2i(-.x, -.y) 58 | def Vec2i::abs(this): Vec2i => Vec2i(abs(.x), abs(.y)) 59 | 60 | def Vec2i::dot(this, other: Vec2i): i32 => .x * other.x + .y * other.y 61 | 62 | def Vec2i::cross(this, other: Vec2i): Vec2i { 63 | return Vec2i( 64 | .y * other.x - .x * other.y, 65 | .x * other.y - .y * other.x 66 | ) 67 | } 68 | 69 | def Vec2i::length(this): f32 => sqrt((.x * .x + .y * .y) as f32) 70 | def Vec2i::length_sq(this): i32 => .x * .x + .y * .y 71 | def Vec2i::dist(this, other: Vec2i): f32 => .sub(other).length() 72 | 73 | def Vec2i::eq(this, other: Vec2i): bool => .x == other.x and .y == other.y 74 | 75 | def Vec2i::shift(this, dir: Direction, amount: i32): Vec2i { 76 | return match dir { 77 | Up => .sub(Vec2i(0, amount)) 78 | Right => .add(Vec2i(amount, 0)) 79 | Down => .add(Vec2i(0, amount)) 80 | Left => .sub(Vec2i(amount, 0)) 81 | } 82 | } 83 | 84 | /// General vector overloads, Vec2f 85 | 86 | def Vec2f::add(this, other: Vec2f): Vec2f => Vec2f(.x + other.x, .y + other.y) 87 | def Vec2f::sub(this, other: Vec2f): Vec2f => Vec2f(.x - other.x, .y - other.y) 88 | def Vec2f::div(this, other: Vec2f): Vec2f => Vec2f(.x / other.x, .y / other.y) 89 | def Vec2f::mult(this, other: Vec2f): Vec2f => Vec2f(.x * other.x, .y * other.y) 90 | 91 | def Vec2f::addf(this, val: f32): Vec2f => Vec2f(.x + val, .y + val) 92 | def Vec2f::subf(this, val: f32): Vec2f => Vec2f(.x - val, .y - val) 93 | def Vec2f::divf(this, val: f32): Vec2f => Vec2f(.x / val, .y / val) 94 | def Vec2f::multf(this, val: f32): Vec2f => Vec2f(.x * val, .y * val) 95 | 96 | def Vec2f::neg(this): Vec2f => Vec2f(-.x, -.y) 97 | def Vec2f::abs(this): Vec2f => Vec2f(absf(.x), absf(.y)) 98 | def Vec2f::dot(this, other: Vec2f): f32 => .x * other.x + .y * other.y 99 | 100 | def Vec2f::cross(this, other: Vec2f): Vec2f { 101 | return Vec2f(.y * other.x - .x * other.y, .x * other.y - .y * other.x) 102 | } 103 | 104 | def Vec2f::length(this): f32 => sqrt(.x * .x + .y * .y) 105 | def Vec2f::length_sq(this): f32 => .x * .x + .y * .y 106 | def Vec2f::dist(this, other: Vec2f): f32 => .sub(other).length() 107 | 108 | def Vec2f::normalized(this): Vec2f => .divf(.length()) 109 | 110 | def Vec2f::eq(this, other: Vec2f): bool => .x == other.x and .y == other.y 111 | 112 | 113 | // Other stuff 114 | 115 | def reduce_degrees(deg: f32): f32 { 116 | if absf(modf(deg, 360.0)) > 180.0 117 | return modf(deg, 360.0) - 360.0 118 | return deg 119 | } 120 | 121 | def Vec2f::rotate(this, rad: f32): Vec2f { 122 | return Vec2f( 123 | .x * cos(rad) - .y * sin(rad), 124 | .x * sin(rad) + .y * cos(rad), 125 | ) 126 | } 127 | 128 | def Vec2f::rotate_deg(this, deg: f32): Vec2f => .rotate(radians(deg)) 129 | -------------------------------------------------------------------------------- /lib/glut.ae: -------------------------------------------------------------------------------- 1 | // macOS 2 | @compiler c_include "GLUT/glut.h" 3 | @compiler c_flag "-framework GLUT -framework OpenGL -Wno-deprecated-declarations" 4 | 5 | // Linux 6 | // @compiler c_include "GL/glut.h" 7 | // @compiler c_flag "-lGL -lGLU -lglut" 8 | 9 | 10 | let GL_COLOR_BUFFER_BIT: i32 extern 11 | let GL_DEPTH_BUFFER_BIT: i32 extern 12 | let GL_POLYGON: i32 extern 13 | let GL_LINES: i32 extern 14 | let GL_DEPTH_TEST: i32 extern 15 | let GLUT_RGB: i32 extern 16 | let GLUT_DOUBLE: i32 extern 17 | let GLUT_DEPTH: i32 extern 18 | 19 | def glutInit(argc: &i32, argv: &string) extern 20 | def glutInitDisplayMode(mode: i32) extern 21 | def glutInitWindowPosition(x: i32, y: i32) extern 22 | def glutInitWindowSize(width: i32, height: i32) extern 23 | def glutCreateWindow(title: string) extern 24 | 25 | def glutDisplayFunc(func: fn()) extern 26 | def glutReshapeFunc(func: fn(i32, i32)) extern 27 | def glutMainLoop() extern 28 | 29 | def glEnable(cap: i32) extern 30 | def glClear(mask: i32) extern 31 | def glBegin(mode: i32) extern 32 | def glColor3f(r: f32, g: f32, b: f32) extern 33 | def glVertex3f(x: f32, y: f32, z: f32) extern 34 | def glEnd() extern 35 | def glFlush() extern 36 | 37 | def glPushMatrix() extern 38 | def glPopMatrix() extern 39 | def glRotatef(angle: f32, x: f32, y: f32, z: f32) extern 40 | def glTranslatef(x: f32, y: f32, z: f32) extern 41 | 42 | def glViewport(x: i32, y: i32, width: i32, height: i32) extern 43 | def glMatrixMode(mode: i32) extern 44 | def glLoadIdentity() extern 45 | 46 | let GL_PROJECTION: i32 extern 47 | let GL_MODELVIEW: i32 extern 48 | 49 | def gluPerspective(fovy: f32, aspect: f32, zNear: f32, zFar: f32) extern 50 | def gluLookAt(ex: f32, ey: f32, ez: f32, 51 | cx: f32, cy: f32, cz: f32, 52 | ux: f32, uy: f32, uz: f32) extern 53 | 54 | 55 | def glutPostRedisplay() extern 56 | def glutTimerFunc(time: i32, func: fn(i32), value: i32) extern 57 | def glutSwapBuffers() extern 58 | def glutWireSphere(radius: f32, slices: i32, stacks: i32) extern -------------------------------------------------------------------------------- /lib/hash/sha1.ae: -------------------------------------------------------------------------------- 1 | 2 | 3 | struct SHA1 {} 4 | 5 | def SHA1::rotate_left(value: u32, bits: u32): u32 { 6 | return (((value) << (bits)) | ((value) >> (32u32 - (bits)))) 7 | } 8 | 9 | def SHA1::hash(input: &Buffer): Buffer { 10 | let data = input.data 11 | let size = input.size as u64 12 | 13 | let H: [u32; 5] 14 | H[0] = 0x67452301 15 | H[1] = 0xEFCDAB89 16 | H[2] = 0x98BADCFE 17 | H[3] = 0x10325476 18 | H[4] = 0xC3D2E1F0 19 | 20 | let loopcount = (size + 8) / 64 + 1 21 | let tailbytes = loopcount * 64 - size 22 | 23 | let datatail: [u8; 128] 24 | set_memory(datatail, 0, 128 * sizeof(u8)) 25 | 26 | datatail[0] = 0x80 27 | for let i = 0u64; i < 8; i += 1 { 28 | datatail[tailbytes - 1 - i] = (((size * 8) >> (i as u64 * 8)) & 0xFF) as u8 29 | } 30 | 31 | let W: [u32; 80] 32 | let didx = 0u64 33 | 34 | for let lidx = 0u64; lidx < loopcount; lidx += 1 { 35 | set_memory(W, 0, 80 * sizeof(u32)) 36 | 37 | for let widx = 0u32; widx < 16; widx += 1 { 38 | let wcount = 24 39 | 40 | while didx < size and wcount >= 0 { 41 | W[widx] += (data[didx] as u32) << wcount as u32 42 | wcount -= 8 43 | didx += 1 44 | } 45 | 46 | while wcount >= 0 { 47 | W[widx] += (datatail[didx - size] as u32) << wcount as u32 48 | wcount -= 8 49 | didx += 1 50 | } 51 | } 52 | 53 | for let widx = 16u32; widx < 32; widx += 1 { 54 | W[widx] = SHA1::rotate_left((W[widx - 3] ^ W[widx - 8] ^ W[widx - 14] ^ W[widx - 16]), 1) 55 | } 56 | 57 | for let widx = 32u32; widx < 80; widx += 1 { 58 | W[widx] = SHA1::rotate_left((W[widx - 6] ^ W[widx - 16] ^ W[widx - 28] ^ W[widx - 32]), 2) 59 | } 60 | 61 | // Main loop 62 | let T: [u32; 5] 63 | copy_memory(T, H, 5 * sizeof(u32)) 64 | 65 | for let idx = 0; idx <= 79; idx += 1 { 66 | let factor: u32 = match idx / 20 { 67 | 0 => 0x5A827999 + ((T[1] & T[2]) | ((~T[1]) & T[3])) 68 | 1 => 0x6ED9EBA1 + ( T[1] ^ T[2] ^ T[3]) 69 | 2 => 0x8F1BBCDC + ((T[1] & T[2]) | (T[1] & T[3]) | (T[2] & T[3])) 70 | 3 => 0xCA62C1D6 + ( T[1] ^ T[2] ^ T[3]) 71 | else => 0 72 | } 73 | let temp = SHA1::rotate_left(T[0], 5) + T[4] + factor + W[idx] 74 | T[4] = T[3] 75 | T[3] = T[2] 76 | T[2] = SHA1::rotate_left(T[1], 30) 77 | T[1] = T[0] 78 | T[0] = temp 79 | } 80 | 81 | for let i = 0; i < 5; i += 1 { 82 | H[i] += T[i] 83 | } 84 | } 85 | 86 | let out = Buffer::make() 87 | out.resize_if_necessary(20) 88 | for let idx = 0; idx < 5; idx += 1 { 89 | out.data[idx * 4 + 0] = (H[idx] >> 24) as u8 90 | out.data[idx * 4 + 1] = (H[idx] >> 16) as u8 91 | out.data[idx * 4 + 2] = (H[idx] >> 8) as u8 92 | out.data[idx * 4 + 3] = (H[idx]) as u8 93 | } 94 | out.size = 20 95 | return out 96 | } 97 | 98 | def SHA1::hash_string(data: string): Buffer { 99 | let input_data = Buffer::from_string(data) 100 | return SHA1::hash(&input_data) 101 | } -------------------------------------------------------------------------------- /lib/image.ae: -------------------------------------------------------------------------------- 1 | // A type that holds an Image, stored in 32-bit floating point format. 2 | // Each of the RGB values are supposed to be in the range [0,1]. 3 | // The `Vec` type is used to represent the pixel colors as well. 4 | 5 | use "lib/vec.ae" 6 | 7 | struct Image { 8 | width: i32 9 | height: i32 10 | data: &Vec 11 | } 12 | 13 | def Image::new(width: i32, height: i32): &Image { 14 | let img = calloc(1, sizeof(Image)) as &Image 15 | *img = Image(width, height, calloc(width * height, sizeof(Vec))) 16 | return img 17 | } 18 | 19 | def Image::free(&this) { 20 | free(.data) 21 | free(this) 22 | } 23 | 24 | def Image::get(&this, x: i32, y: i32): Vec => .data[y * .width + x] 25 | def Image::set(&this, x: i32, y: i32, col: Vec) { .data[y * .width + x] = col } 26 | 27 | def Image::save(&this, filename: string) { 28 | let file = File::open(filename, "wb") 29 | defer file.close() 30 | 31 | let u8buf = calloc(1, .width * .height * 3 * sizeof(u8)) as &u8 32 | defer free(u8buf) 33 | 34 | for let i = 0; i < .width * .height; i += 1 { 35 | let col = *(.data + i) 36 | let offset = u8buf + i * 3 37 | *(offset + 0) = (clamp01(col.x) * 255.0) as u8 38 | *(offset + 1) = (clamp01(col.y) * 255.0) as u8 39 | *(offset + 2) = (clamp01(col.z) * 255.0) as u8 40 | } 41 | 42 | let header = `P6 {.width} {.height} 255\n` 43 | file.write(header , header.len()) 44 | file.write(u8buf , 3 * .width * .height) 45 | } -------------------------------------------------------------------------------- /lib/json.ae: -------------------------------------------------------------------------------- 1 | // This just reuses the lexer from the compiler, 2 | // since that's already capable of lexing JSON. 3 | use "compiler/lexer.ae" 4 | use "lib/buffer.ae" 5 | use "lib/value.ae" 6 | 7 | struct JSONParser { 8 | tokens: &Vector 9 | curr: i32 10 | } 11 | 12 | def JSONParser::make(tokens: &Vector): JSONParser { 13 | let parser: JSONParser 14 | parser.tokens = tokens 15 | parser.curr = 0 16 | return parser 17 | } 18 | 19 | def JSONParser::token(&this): &Token => .tokens.at(.curr) 20 | 21 | def JSONParser::consume(&this, type: TokenType): &Token { 22 | if .token().type != type { 23 | println("Expected %s but got %s", type.str(), .token().type.str()) 24 | exit(1) 25 | } 26 | let tok = .token() 27 | .curr += 1 28 | return tok 29 | } 30 | 31 | def JSONParser::parse_object(&this): &Value { 32 | .consume(TokenType::OpenCurly) 33 | let json = Value::new(ValueType::Dictionary) 34 | while .token().type != TokenType::CloseCurly { 35 | let key = .consume(TokenType::StringLiteral) 36 | .consume(TokenType::Colon) 37 | let value = .parse_value() 38 | json.u.as_dict.insert(key.text, value) 39 | if .token().type == TokenType::Comma { 40 | .consume(TokenType::Comma) 41 | } 42 | } 43 | .consume(TokenType::CloseCurly) 44 | return json 45 | } 46 | 47 | def JSONParser::parse_array(&this): &Value { 48 | .consume(TokenType::OpenSquare) 49 | let json = Value::new(ValueType::List) 50 | while .token().type != TokenType::CloseSquare { 51 | let value = .parse_value() 52 | json.u.as_list.push(value) 53 | if .token().type == TokenType::Comma { 54 | .consume(TokenType::Comma) 55 | } 56 | } 57 | .consume(TokenType::CloseSquare) 58 | return json 59 | } 60 | 61 | def JSONParser::parse_value(&this): &Value => match .token().type { 62 | Null => { 63 | .consume(TokenType::Null) 64 | yield Value::new(ValueType::Null) 65 | } 66 | True | False => { 67 | let json = Value::new(ValueType::Bool) 68 | let tok = .token() 69 | json.u.as_bool = tok.text.eq("true") 70 | .curr += 1 71 | yield json 72 | } 73 | IntLiteral => { 74 | let json = Value::new(ValueType::Integer) 75 | let tok = .consume(TokenType::IntLiteral) 76 | json.u.as_num = tok.text.to_i32() as i64 77 | yield json 78 | } 79 | StringLiteral => { 80 | let json = Value::new(ValueType::String) 81 | json.u.as_str = Buffer::from_string(.consume(TokenType::StringLiteral).text) 82 | yield json 83 | } 84 | OpenCurly => .parse_object() 85 | OpenSquare => .parse_array() 86 | else => { 87 | println("Unexpected token in JSONParser::parse_value: %s", .token().type.str()) 88 | exit(1) 89 | } 90 | } 91 | 92 | def JSONParser::parse(&this): &Value => match .token().type { 93 | OpenCurly => .parse_object() 94 | OpenSquare => .parse_array() 95 | else => { 96 | println("Expected { or [ at JSON top level") 97 | exit(1) 98 | } 99 | } 100 | 101 | //////////////////////////////////////////////////////////////////////////////// 102 | 103 | // namespace JSON 104 | struct JSON {} 105 | 106 | def JSON::parse(source: string, filename: string): &Value { 107 | let lexer = Lexer::make(source, filename) 108 | let tokens = lexer.lex() 109 | let parser = JSONParser::make(tokens) 110 | return parser.parse() 111 | } 112 | 113 | def JSON::parse_from_string(json_str: string): &Value => JSON::parse(json_str, "") 114 | 115 | def JSON::parse_from_file(filename: string): &Value { 116 | let file = File::open(filename, "r") 117 | let source = file.slurp() 118 | file.close() 119 | return JSON::parse(source, filename) 120 | } 121 | 122 | def JSON::serialize_into(val: &Value, sb: &Buffer) { 123 | match val.type { 124 | Null => sb.puts("null") 125 | Bool => sb.puts(if val.u.as_bool "true" else "false") 126 | Integer => sb.putsf(`{val.u.as_num}`) 127 | String => { 128 | sb.puts("\"") 129 | // Want to escape non-ASCII characters here 130 | let buf = val.u.as_str 131 | for let i = 0; i < buf.size; i += 1 { 132 | let c = buf.data[i] as char 133 | if is_printable(c) { 134 | if c == '\\' or c == '"' { 135 | sb.putsf(`\\{c}`) 136 | } else { 137 | sb.putc(c) 138 | } 139 | } else { 140 | sb.putsf(`\\x{buf.data[i]:02x}`) 141 | } 142 | } 143 | sb.puts("\"") 144 | } 145 | List => { 146 | sb.puts("[") 147 | let lst = val.u.as_list 148 | for let i = 0; i < lst.size; i += 1 { 149 | let value = lst.at(i) as &Value 150 | if i > 0 { 151 | sb.puts(", ") 152 | } 153 | JSON::serialize_into(value, sb) 154 | } 155 | sb.puts("]") 156 | } 157 | Dictionary => { 158 | sb.puts("{") 159 | let first = true 160 | for let iter = val.u.as_dict.iter(); iter.cur?; iter.next() { 161 | if not first { 162 | sb.puts(", ") 163 | } 164 | first = false 165 | sb.puts("\"") 166 | sb.puts(iter.key()) 167 | sb.puts("\": ") 168 | let value = iter.value() as &Value 169 | JSON::serialize_into(value, sb) 170 | } 171 | sb.puts("}") 172 | } 173 | } 174 | } 175 | 176 | def JSON::serialize(val: &Value): Buffer { 177 | let sb = Buffer::make() 178 | JSON::serialize_into(val, &sb) 179 | return sb 180 | } 181 | -------------------------------------------------------------------------------- /lib/map.ae: -------------------------------------------------------------------------------- 1 | // A simple hash-map that maps strings to arbitrary objects. 2 | 3 | use "lib/vector.ae" 4 | 5 | struct MapNode { 6 | key: string 7 | value: untyped_ptr 8 | next: &MapNode 9 | } 10 | 11 | def MapNode::new(key: string, value: untyped_ptr, next: &MapNode): &MapNode { 12 | let node = calloc(1, sizeof(MapNode)) as &MapNode 13 | node.key = key 14 | node.value = value 15 | node.next = next 16 | return node 17 | } 18 | 19 | def MapNode::free_list(node: &MapNode) { 20 | let cur = node 21 | while cur? { 22 | let next = cur.next 23 | free(cur) 24 | cur = next 25 | } 26 | } 27 | 28 | struct Map { 29 | buckets: &&MapNode 30 | num_items: i32 31 | num_buckets: i32 32 | num_collisions: i32 33 | } 34 | 35 | def Map::new(): &Map { 36 | let map = calloc(1, sizeof(Map)) as &Map 37 | map.num_buckets = 4 38 | map.buckets = calloc(map.num_buckets, sizeof(&MapNode)) as &&MapNode 39 | return map 40 | } 41 | 42 | def Map::hash(&this, s: string): i32 { 43 | let hash = 5381 44 | let len = s.len() 45 | for let i = 0; i < len; i += 1 { 46 | hash = hash * 33 ^ s[i] as i32 47 | } 48 | hash = hash % .num_buckets 49 | if hash < 0 { 50 | hash += .num_buckets 51 | } 52 | return hash 53 | } 54 | 55 | def Map::get_node(&this, key: string): &MapNode { 56 | let hash = .hash(key) 57 | let node = .buckets[hash] 58 | while node? { 59 | if node.key.eq(key) { 60 | return node 61 | } 62 | node = node.next 63 | } 64 | return null 65 | } 66 | 67 | def Map::get(&this, key: string): untyped_ptr { 68 | let node = .get_node(key) 69 | if node? { 70 | return node.value 71 | } 72 | return null 73 | } 74 | 75 | def Map::exists(&this, key: string): bool { 76 | return .get_node(key)? 77 | } 78 | 79 | def Map::insert(&this, key: string, value: untyped_ptr) { 80 | let node = .get_node(key) 81 | if node? { 82 | node.value = value 83 | } else { 84 | let hash = .hash(key) 85 | let new_node = MapNode::new(key, value, .buckets[hash]) 86 | if .buckets[hash]? { 87 | .num_collisions += 1 88 | } 89 | .buckets[hash] = new_node 90 | .num_items += 1 91 | if .num_items > .num_buckets { 92 | .resize() 93 | } 94 | } 95 | } 96 | 97 | def Map::resize(&this) { 98 | let old_buckets = .buckets 99 | let old_num_buckets = .num_buckets 100 | let old_num_items = .num_items 101 | .num_collisions = 0 102 | .num_buckets *= 2 103 | .buckets = calloc(.num_buckets, sizeof(&MapNode)) as &&MapNode 104 | for let i = 0; i < old_num_buckets; i += 1 { 105 | let node = old_buckets[i] 106 | while node? { 107 | let new_hash = .hash(node.key) 108 | let new_node = MapNode::new(node.key, node.value, .buckets[new_hash]) 109 | if .buckets[new_hash]? { 110 | .num_collisions += 1 111 | } 112 | .buckets[new_hash] = new_node 113 | node = node.next 114 | } 115 | } 116 | for let i = 0; i < old_num_buckets; i += 1 { 117 | MapNode::free_list(old_buckets[i]) 118 | } 119 | free(old_buckets) 120 | } 121 | 122 | def Map::print_keys(&this) { 123 | for let i = 0; i < .num_buckets; i += 1 { 124 | let node = .buckets[i] 125 | while node? { 126 | println("- '%s'\n", node.key) 127 | node = node.next 128 | } 129 | } 130 | } 131 | 132 | def Map::push_keys(&this, vec: &Vector) { 133 | for let i = 0; i < .num_buckets; i += 1 { 134 | let node = .buckets[i] 135 | while node? { 136 | vec.push(node.key) 137 | node = node.next 138 | } 139 | } 140 | } 141 | 142 | def Map::free(&this) { 143 | for let i = 0; i < .num_buckets; i += 1 { 144 | MapNode::free_list(.buckets[i]) 145 | } 146 | free(.buckets) 147 | } 148 | 149 | def Map::iter(&this): MapIterator { 150 | return MapIterator::make(this) 151 | } 152 | 153 | struct MapIterator { 154 | idx: i32 155 | cur: &MapNode 156 | map: &Map 157 | } 158 | 159 | def MapIterator::key(&this): string { 160 | return .cur.key 161 | } 162 | 163 | def MapIterator::value(&this): untyped_ptr { 164 | return .cur.value 165 | } 166 | 167 | def MapIterator::make(map: &Map): MapIterator { 168 | let it = MapIterator(idx: -1, cur: null, map) 169 | it.next() 170 | return it 171 | } 172 | 173 | def MapIterator::next(&this) { 174 | while .idx < .map.num_buckets { 175 | while .cur? { 176 | .cur = .cur.next 177 | if .cur? return 178 | } 179 | .idx += 1 180 | .cur = if .idx < .map.num_buckets { 181 | yield .map.buckets[.idx] 182 | } else { 183 | yield null 184 | } 185 | if .cur? return 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /lib/math.ae: -------------------------------------------------------------------------------- 1 | @compiler c_flag "-lm" 2 | @compiler c_include "math.h" 3 | 4 | def sqrt(x: f32): f32 extern 5 | def cos(x: f32): f32 extern 6 | def sin(x: f32): f32 extern 7 | def tan(x: f32): f32 extern 8 | def atan2(x: f32, y: f32): f32 extern 9 | def rand01(): f32 extern("drand48") 10 | def randint(): i32 extern("rand") 11 | 12 | def minf(a: f32, b: f32): f32 { if a < b return a; else return b; } 13 | def maxf(a: f32, b: f32): f32 { if a > b return a; else return b; } 14 | 15 | def clampf(x: f32, min: f32, max: f32): f32 { return maxf(minf(x, max), min); } 16 | def clamp01(x: f32): f32 { return clampf(x, 0.0, 1.0); } 17 | 18 | let PI: f32 extern("M_PI") 19 | def degrees(radians: f32): f32 { return radians * 180.0 / PI; } 20 | def radians(degrees: f32): f32 { return degrees * PI / 180.0; } -------------------------------------------------------------------------------- /lib/prelude.ae: -------------------------------------------------------------------------------- 1 | @compiler c_include "stdio.h" 2 | @compiler c_include "stdlib.h" 3 | @compiler c_include "stdbool.h" 4 | @compiler c_include "stdint.h" 5 | @compiler c_include "string.h" 6 | @compiler c_include "errno.h" 7 | @compiler c_embed_header "lib/prelude.h" 8 | 9 | @compiler c_include "ctype.h" 10 | def is_alpha(c: char): bool extern("isalpha") 11 | def is_digit(c: char): bool extern("isdigit") 12 | def is_alnum(c: char): bool extern("isalnum") 13 | def is_printable(c: char): bool extern("isprint") 14 | 15 | def malloc(size: i32): untyped_ptr extern 16 | def realloc(old: untyped_ptr, size: i32): untyped_ptr extern 17 | def calloc(size: i32, num: i32): untyped_ptr extern 18 | def free(ptr: untyped_ptr) extern 19 | 20 | let errno: i32 extern 21 | 22 | let SEEK_END: i32 extern 23 | let SEEK_SET: i32 extern 24 | let SEEK_CUR: i32 extern 25 | 26 | struct File extern("FILE") 27 | def File::close(&this) extern("fclose") 28 | def File::tell(&this): i32 extern("ftell") 29 | def File::seek(&this, offset: i32, whence: i32): i32 extern("fseek") 30 | 31 | def File::open(path: string, mode: string): &File { 32 | let file = _c_fopen(path, mode) 33 | if not file? { 34 | println("Error opening file '%s': %s", path, strerror(errno)) 35 | exit(1) 36 | } 37 | return file 38 | } 39 | 40 | def File::exists(path: string): bool { 41 | let file = _c_fopen(path, "r") 42 | if not file? { 43 | return false 44 | } 45 | file.close() 46 | return true 47 | } 48 | 49 | def File::size(&this): i32 { 50 | let pos = this.tell() 51 | this.seek(0, SEEK_END) 52 | let size = this.tell() 53 | this.seek(pos, SEEK_SET) 54 | return size 55 | } 56 | 57 | def File::slurp(&this): string { 58 | let pos = .tell() 59 | .seek(0, SEEK_END) 60 | let size = .tell() 61 | .seek(pos, SEEK_SET) 62 | let buf = calloc((size+1), sizeof(char)) 63 | _c_fread(buf, 1, size, this) 64 | .seek(pos, SEEK_SET) 65 | return buf as string 66 | } 67 | 68 | def File::read(&this, buf: untyped_ptr, size: i32): i32 { 69 | return _c_fread(buf, 1, size, this) 70 | } 71 | 72 | def File::write(&this, buf: untyped_ptr, size: i32): i32 { 73 | return _c_fwrite(buf, 1, size, this) 74 | } 75 | 76 | def File::puts(&this, str: string) { 77 | _c_fwrite(str , 1, str.len(), this) 78 | } 79 | 80 | def exit(code: i32) exits extern 81 | def panic(msg: string) exits { 82 | println("%s", msg) 83 | exit(1) 84 | } 85 | 86 | def string::len(this): i32 extern("strlen") 87 | def string::compare(this, str2: string): i32 extern("strcmp") 88 | def string::compare_n(this, str2: string, n: i32): i32 extern("strncmp") 89 | def string::copy(this): string extern("strdup") 90 | def string::concat(this, src: string): string extern("strcat") 91 | 92 | def copy_memory(dest: untyped_ptr, src: untyped_ptr, size: i32) extern("memcpy") 93 | def set_memory(ptr: untyped_ptr, val: u8, size: i32) extern("memset") 94 | 95 | 96 | def string::starts_with(this, prefix: string): bool { 97 | let prefix_len = prefix.len() 98 | if .len() < prefix_len { 99 | return false 100 | } 101 | return .compare_n(prefix, prefix_len) == 0 102 | } 103 | 104 | def string::ends_with(this, suffix: string): bool { 105 | let suffix_len = suffix.len() 106 | if .len() < suffix_len { 107 | return false 108 | } 109 | return .compare_n(suffix, suffix_len) == 0 110 | } 111 | 112 | def string::remove_last_n(this, n: i32) { 113 | this[.len() - n] = '\0' 114 | } 115 | 116 | def string::eq(this, str2: string): bool => this.compare(str2) == 0 117 | 118 | def string::substring(this, start: i32, len: i32): string { 119 | let new_str = calloc(len + 1, sizeof(char)) as string 120 | copy_memory(new_str, this + start, len) 121 | return new_str 122 | } 123 | 124 | def string::strip_trailing_whitespace(this) { 125 | for let i = .len() - 1; i >= 0; i -= 1 { 126 | if this[i] != ' ' break 127 | this[i] = '\0' 128 | } 129 | } 130 | 131 | def string::to_i32(this): i32 extern("atoi") 132 | 133 | def strerror(errno: i32): string extern 134 | 135 | def puts(str: string) extern 136 | def system(str: string): i32 extern 137 | 138 | def get_environment_variable(name: string): string extern("getenv") 139 | 140 | def min(a: i32, b: i32): i32 => if a < b then a else b 141 | def max(a: i32, b: i32): i32 => if a > b then a else b 142 | 143 | /// Internal stuff 144 | 145 | def _c_fopen(path: string, mode: string): &File extern("fopen") 146 | def _c_fread(ptr: untyped_ptr, size: i32, num: i32, file: &File): i32 extern("fread") 147 | def _c_fwrite(ptr: untyped_ptr, size: i32, num: i32, file: &File): i32 extern("fwrite") 148 | -------------------------------------------------------------------------------- /lib/prelude.h: -------------------------------------------------------------------------------- 1 | #include "stdarg.h" 2 | 3 | typedef int8_t i8; 4 | typedef int16_t i16; 5 | typedef int32_t i32; 6 | typedef int64_t i64; 7 | 8 | typedef uint8_t u8; 9 | typedef uint16_t u16; 10 | typedef uint32_t u32; 11 | typedef uint64_t u64; 12 | 13 | typedef float f32; 14 | typedef double f64; 15 | 16 | char* format_string(const char* format, ...) { 17 | va_list args; 18 | va_start(args, format); 19 | int size = vsnprintf(NULL, 0, format, args); 20 | va_end(args); 21 | va_start(args, format); 22 | char* s = calloc(1, size + 1); 23 | vsprintf(s, format, args); 24 | s[size] = '\0'; 25 | va_end(args); 26 | return s; 27 | } 28 | -------------------------------------------------------------------------------- /lib/sdl.ae: -------------------------------------------------------------------------------- 1 | @compiler c_include "SDL2/SDL.h" 2 | @compiler c_include "SDL2/SDL_image.h" 3 | @compiler c_include "SDL2/SDL_mixer.h" 4 | @compiler c_include "SDL2/SDL_ttf.h" 5 | @compiler c_flag "-lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer" 6 | 7 | struct SDLWindow extern("SDL_Window") 8 | struct SDLRenderer extern("SDL_Renderer") 9 | 10 | struct SDLTexture extern("SDL_Texture") 11 | def SDLTexture::destroy(&this) extern("SDL_DestroyTexture") 12 | 13 | struct SDLRect extern("SDL_Rect") { 14 | x: i32 15 | y: i32 16 | w: i32 17 | h: i32 18 | } 19 | 20 | struct SDLSurface extern("SDL_Surface") 21 | def SDLSurface::destroy(&this) extern("SDL_FreeSurface") 22 | 23 | struct SDLColor extern("SDL_Color") { 24 | r: i32 // Not really i32, but it's convenient 25 | g: i32 26 | b: i32 27 | a: i32 28 | } 29 | 30 | enum Key extern("SDL_Keycode") { 31 | A = extern("SDLK_a") 32 | B = extern("SDLK_b") 33 | C = extern("SDLK_c") 34 | D = extern("SDLK_d") 35 | E = extern("SDLK_e") 36 | F = extern("SDLK_f") 37 | G = extern("SDLK_g") 38 | H = extern("SDLK_h") 39 | I = extern("SDLK_i") 40 | J = extern("SDLK_j") 41 | K = extern("SDLK_k") 42 | L = extern("SDLK_l") 43 | M = extern("SDLK_m") 44 | N = extern("SDLK_n") 45 | O = extern("SDLK_o") 46 | P = extern("SDLK_p") 47 | Q = extern("SDLK_q") 48 | R = extern("SDLK_r") 49 | S = extern("SDLK_s") 50 | T = extern("SDLK_t") 51 | U = extern("SDLK_u") 52 | V = extern("SDLK_v") 53 | W = extern("SDLK_w") 54 | X = extern("SDLK_x") 55 | Y = extern("SDLK_y") 56 | Z = extern("SDLK_z") 57 | 58 | Up = extern("SDLK_UP") 59 | Down = extern("SDLK_DOWN") 60 | Left = extern("SDLK_LEFT") 61 | Right = extern("SDLK_RIGHT") 62 | Space = extern("SDLK_SPACE") 63 | Escape = extern("SDLK_ESCAPE") 64 | } 65 | 66 | struct SDLKeysym extern("SDL_Keysym") { 67 | scancode: i32 68 | sym: Key 69 | mod: i32 70 | } 71 | 72 | enum EventType extern("SDL_EventType") { 73 | Quit = extern("SDL_QUIT") 74 | KeyDown = extern("SDL_KEYDOWN") 75 | KeyUp = extern("SDL_KEYUP") 76 | } 77 | 78 | struct SDLKeyboardEvent extern("SDL_KeyboardEvent") { 79 | type: i32 80 | keysym: SDLKeysym 81 | } 82 | 83 | struct SDLEvent extern("SDL_Event") { 84 | type: EventType 85 | key: SDLKeyboardEvent 86 | } 87 | 88 | const SDL_INIT_EVERYTHING: i32 extern 89 | const SDL_PIXELFORMAT_ABGR8888: i32 extern 90 | const SDL_TEXTUREACCESS_STREAMING: i32 extern 91 | const SDL_MIX_MAXVOLUME: i32 extern 92 | 93 | // Just a namespace 94 | struct SDL {} 95 | 96 | def SDL::init(flags: i32) extern("SDL_Init") 97 | def SDL::quit() extern("SDL_Quit") 98 | def SDL::create_window_renderer( 99 | width: i32, 100 | height: i32, 101 | flags: i32, window: &&SDLWindow, 102 | renderer: &&SDLRenderer) extern("SDL_CreateWindowAndRenderer") 103 | def SDL::delay(ms: i32) extern("SDL_Delay") 104 | def SDL::get_mouse_state(x: &i32, y: &i32): i32 extern("SDL_GetMouseState") 105 | def SDL::show_cursor(toggle: bool) extern("SDL_ShowCursor") 106 | 107 | def SDLRenderer::clear(&this) extern("SDL_RenderClear") 108 | def SDLRenderer::present(&this) extern("SDL_RenderPresent") 109 | def SDLRenderer::copy(&this, texture: &SDLTexture, src: &SDLRect, dst: &SDLRect) extern("SDL_RenderCopy") 110 | def SDLRenderer::copy_ex(&this, texture: &SDLTexture, src: &SDLRect, dst: &SDLRect, angle: f32, center: untyped_ptr, flip: i32) extern("SDL_RenderCopyEx") 111 | def SDLRenderer::set_draw_color(&this, r: i32, g: i32, b: i32, a: i32) extern("SDL_SetRenderDrawColor") 112 | def SDLRenderer::destroy(&this) extern("SDL_DestroyRenderer") 113 | def SDLRenderer::load_image(&this, path: string): &SDLTexture extern("IMG_LoadTexture") 114 | def SDLRenderer::create_texture_from_surface(&this, surface: &SDLSurface): &SDLTexture extern("SDL_CreateTextureFromSurface") 115 | def SDLRenderer::create_texture(&this, format: i32, access: i32, w: i32, h: i32): &SDLTexture extern("SDL_CreateTexture") 116 | 117 | def SDLWindow::destroy(&this) extern("SDL_DestroyWindow") 118 | 119 | // FIXME: what's a and b? 120 | def SDLTexture::query(&this, a: untyped_ptr, b: untyped_ptr, w: &i32, h: &i32) extern("SDL_QueryTexture") 121 | def SDLTexture::lock(&this, rect: &SDLRect, pixels: &untyped_ptr, pitch: &i32) extern("SDL_LockTexture") 122 | def SDLTexture::unlock(&this) extern("SDL_UnlockTexture") 123 | 124 | def SDL::poll_event(event: &SDLEvent): bool extern("SDL_PollEvent") 125 | 126 | // SDL2_TTF 127 | 128 | struct TTFFont extern("TTF_Font") 129 | 130 | def TTFFont::load(path: string, size: i32): &TTFFont extern("TTF_OpenFont") 131 | def TTFFont::render_solid(&this, text: string, color: SDLColor): &SDLSurface extern("TTF_RenderText_Solid") 132 | 133 | // Namespace 134 | struct TTF {} 135 | 136 | def TTF::init(): i32 extern("TTF_Init") 137 | def TTF::get_error(): string extern("TTF_GetError") 138 | 139 | // Sound BS 140 | 141 | struct MIX {} 142 | 143 | def MIX::open_audio(frequency: i32, format: u16, channels: i32, chunksize: i32): i32 extern("Mix_OpenAudio") 144 | const MIX_DEFAULT_FORMAT: u16 extern 145 | 146 | struct MixChunk extern("Mix_Chunk") 147 | struct MixMusic extern("Mix_Music") 148 | 149 | def MIX::load_music(path: string): &MixMusic extern("Mix_LoadMUS") 150 | def MIX::load_wav(path: string): &MixChunk extern("Mix_LoadWAV") 151 | def MIX::play_music(music: &MixMusic, loops: i32): i32 extern("Mix_PlayMusic") 152 | def MIX::play_channel(channel: i32, chunk: &MixChunk, loops: i32): i32 extern("Mix_PlayChannel") 153 | def MIX::allocate_channels(num: i32): i32 extern("Mix_AllocateChannels") 154 | 155 | def MIX::pause(channel: i32) extern("Mix_Pause") 156 | def MIX::resume(channel: i32) extern("Mix_Resume") 157 | 158 | def MIX::free_chunk(chunk: &MixChunk) extern("Mix_FreeChunk") 159 | def MIX::free_music(music: &MixMusic) extern("Mix_FreeMusic") 160 | def MIX::quit() extern("Mix_Quit") 161 | 162 | def MIX::get_error(): string extern("Mix_GetError") 163 | def MIX::volume(channel: i32, volume: i32) extern("Mix_Volume") -------------------------------------------------------------------------------- /lib/socket.ae: -------------------------------------------------------------------------------- 1 | // Public facing API 2 | 3 | struct Socket { 4 | fd: i32 5 | } 6 | 7 | enum SocketMode { 8 | UDP 9 | TCP 10 | } 11 | 12 | def Socket::connect(host: string, port: i32, mode: SocketMode): Socket { 13 | let sock: Socket 14 | 15 | // Creating socket 16 | sock.fd = match mode { 17 | UDP => _c_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP), 18 | TCP => _c_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) 19 | } 20 | if (sock.fd < 0){ 21 | println(`Error creating socket: {strerror(errno)}`) 22 | exit(1) 23 | } 24 | 25 | // Adding server address info. to struct 26 | let server_address: SockAddrIn 27 | server_address.sin_family = AF_INET; 28 | server_address.sin_port = _c_htons(port); 29 | 30 | let hostm = _c_gethostbyname(host) 31 | if not hostm? or not hostm.h_addr? { 32 | panic(`Error getting host by name, exiting`) 33 | } 34 | server_address.sin_addr.s_addr = *(hostm.h_addr as &i32) 35 | 36 | // Assigning IP address to struct 37 | if (_c_inet_pton(AF_INET, host, &server_address.sin_addr) < 0) { 38 | println(`inet_pton had an error: {strerror(errno)}`) 39 | exit(1) 40 | } 41 | 42 | // Connecting to the socket 43 | if (_c_connect(sock.fd, (&server_address) as &SockAddr, sizeof(SockAddrIn)) < 0) { 44 | println(`Error connecting to socket: {strerror(errno)}`) 45 | exit(1) 46 | } 47 | 48 | return sock 49 | } 50 | 51 | def Socket::read(&this, buf: &Buffer, max_size: i32): i32 { 52 | buf.resize_if_necessary(max_size) 53 | buf.size = _c_read(this.fd, buf.data, max_size) 54 | return buf.size 55 | } 56 | 57 | def Socket::read_exactly(&this, buf: &Buffer, size: i32): i32 { 58 | buf.resize_if_necessary(size) 59 | buf.size = 0 60 | while size > 0 { 61 | let n = _c_read(this.fd, buf.data + buf.size, size) 62 | if n < 0 { 63 | println(`Error reading from socket: {strerror(errno)}`) 64 | return -1 65 | } 66 | buf.size += n 67 | size -= n 68 | } 69 | return buf.size 70 | } 71 | 72 | def Socket::write(&this, buf: &Buffer): i32 => _c_write(this.fd, buf.data, buf.size) 73 | 74 | 75 | /// Internal stuff 76 | 77 | @compiler c_include "sys/socket.h" 78 | @compiler c_include "netinet/in.h" 79 | @compiler c_include "arpa/inet.h" 80 | @compiler c_include "unistd.h" 81 | @compiler c_include "errno.h" 82 | @compiler c_include "sys/types.h" 83 | @compiler c_include "time.h" 84 | @compiler c_include "netdb.h" 85 | 86 | struct SockAddr extern("struct sockaddr") 87 | struct HostEnt extern("struct hostent") { 88 | h_addr: &u8 // this type is a lie 89 | } 90 | struct SinAddr extern("struct in_addr") { 91 | s_addr: i32 92 | } 93 | struct SockAddrIn extern("struct sockaddr_in") { 94 | sin_family: i32 95 | sin_port: i32 96 | sin_addr: SinAddr 97 | } 98 | 99 | def _c_htons(val: i32): i32 extern("htons") 100 | def _c_htonl(val: i32): i32 extern("htonl") 101 | 102 | let AF_INET: i32 extern 103 | let SOCK_STREAM: i32 extern 104 | let INADDR_ANY: i32 extern 105 | let IPPROTO_UDP: i32 extern 106 | let IPPROTO_TCP: i32 extern 107 | let SOCK_DGRAM: i32 extern 108 | 109 | def _c_socket(domain: i32, type: i32, protocol: i32): i32 extern("socket") 110 | def _c_bind(sockfd: i32, addr: &SockAddr, addrlen: i32): i32 extern("bind") 111 | def _c_listen(sockfd: i32, backlog: i32): i32 extern("listen") 112 | def _c_accept(sockfd: i32, addr: &SockAddr, addrlen: &i32): i32 extern("accept") 113 | def _c_inet_pton(af: i32, src: string, dst: untyped_ptr): i32 extern("inet_pton") 114 | def _c_connect(sockfd: i32, addr: &SockAddr, addrlen: i32): i32 extern("connect") 115 | def _c_gethostbyname(name: string): &HostEnt extern("gethostbyname") 116 | 117 | def _c_read(fd: i32, buf: untyped_ptr, count: i32): i32 extern("read") 118 | def _c_write(fd: i32, buf: untyped_ptr, count: i32): i32 extern("write") 119 | def _c_close(fd: i32): i32 extern("close") -------------------------------------------------------------------------------- /lib/span.ae: -------------------------------------------------------------------------------- 1 | struct Location { 2 | filename: string 3 | line: i32 4 | col: i32 5 | index: i32 6 | } 7 | 8 | def Location::str(this): string => `{.filename}:{.line}:{.col}` 9 | 10 | def Location::is_before(&this, other: Location): bool { 11 | if .line > other.line return false 12 | if .line < other.line return true 13 | return .col <= other.col 14 | } 15 | 16 | struct Span { 17 | start: Location 18 | end: Location 19 | } 20 | 21 | def Span::str(this): string => `{.start.str()} => {.end.str()}` 22 | 23 | def Span::default(): Span { 24 | let span: Span 25 | span.start = Location(filename: "", line: 0, col: 0, index: 0) 26 | span.end = Location(filename: "", line: 0, col: 0, index: 0) 27 | return span 28 | } 29 | 30 | // Needs to be called in the correct order! 31 | def Span::join(this, other: Span): Span { 32 | let span: Span 33 | span.start = this.start 34 | span.end = other.end 35 | return span 36 | } 37 | 38 | def Span::contains_loc(this, loc: Location): bool { 39 | if not .start.filename.eq(loc.filename) return false 40 | return .start.is_before(loc) and loc.is_before(.end) 41 | } -------------------------------------------------------------------------------- /lib/value.ae: -------------------------------------------------------------------------------- 1 | // A dynamically typed value, useful for JSON + other stuff 2 | use "lib/vector.ae" 3 | use "lib/map.ae" 4 | use "lib/json.ae" 5 | use "lib/buffer.ae" 6 | use "lib/span.ae" 7 | 8 | enum ValueType { 9 | Null 10 | Bool 11 | Integer 12 | String 13 | List 14 | Dictionary 15 | } 16 | 17 | def ValueType::str(this): string => match this { 18 | Null => "Null" 19 | Bool => "Bool" 20 | Integer => "Integer" 21 | String => "String" 22 | List => "List" 23 | Dictionary => "Dictionary" 24 | } 25 | 26 | union ValueUnion { 27 | as_bool: bool 28 | as_num: i64 29 | as_str: Buffer 30 | as_list: &Vector // &Vector 31 | as_dict: &Map // &Map 32 | } 33 | 34 | struct Value { 35 | type: ValueType 36 | u: ValueUnion 37 | span: Span 38 | } 39 | 40 | def Value::new(type: ValueType): &Value { 41 | let val = calloc(1, sizeof(Value)) as &Value 42 | val.type = type 43 | match type { 44 | Dictionary => val.u.as_dict = Map::new() 45 | List => val.u.as_list = Vector::new() 46 | else => {} 47 | } 48 | val.span = Span::default() 49 | return val 50 | } 51 | 52 | def Value::new_str(str: Buffer): &Value { 53 | let val = Value::new(String) 54 | val.u.as_str = str 55 | return val 56 | } 57 | 58 | def Value::new_bool(bul: bool): &Value { 59 | let val = Value::new(Bool) 60 | val.u.as_bool = bul 61 | return val 62 | } 63 | 64 | def Value::new_number(num: i64): &Value { 65 | let val = Value::new(Integer) 66 | val.u.as_num = num 67 | return val 68 | } 69 | 70 | def Value::new_array(vec: &Vector): &Value { 71 | let val = Value::new(List) 72 | free(val.u.as_list) 73 | val.u.as_list = vec 74 | return val 75 | } 76 | 77 | def Value::new_dictect(map: &Map): &Value { 78 | let val = Value::new(Dictionary) 79 | free(val.u.as_dict) 80 | val.u.as_dict = map 81 | return val 82 | } 83 | 84 | def Value::ensure(&this, type: ValueType) { 85 | if .type != type { 86 | println("Value type mismatch, expected %s but got %s", .type.str(), type.str()) 87 | exit(1) 88 | } 89 | } 90 | 91 | def Value::is(this, type: ValueType): bool => .type == type 92 | 93 | def Value::at(&this, idx: u64): &Value { 94 | .ensure(List) 95 | return .u.as_list.at(idx as i32) 96 | } 97 | 98 | def Value::set(&this, idx: i64, value: &Value) { 99 | .ensure(List) 100 | .u.as_list.data[idx] = value 101 | } 102 | 103 | def Value::push(&this, value: &Value) { 104 | .ensure(List) 105 | .u.as_list.push(value) 106 | } 107 | 108 | def Value::get(&this, key: string): &Value { 109 | .ensure(Dictionary) 110 | return .u.as_dict.get(key) 111 | } 112 | 113 | def Value::insert(&this, key: string, value: &Value) { 114 | .ensure(Dictionary) 115 | .u.as_dict.insert(key, value) 116 | } 117 | 118 | def Value::as_bool(&this): bool { 119 | .ensure(Bool) 120 | return .u.as_bool 121 | } 122 | 123 | def Value::as_num(&this): i64 { 124 | .ensure(Integer) 125 | return .u.as_num 126 | } 127 | 128 | def Value::as_str(&this): Buffer { 129 | .ensure(String) 130 | return .u.as_str 131 | } 132 | 133 | def Value::as_arr(&this): &Vector { 134 | .ensure(List) 135 | return .u.as_list 136 | } 137 | 138 | def Value::as_dict(&this): &Map { 139 | .ensure(Dictionary) 140 | return .u.as_dict 141 | } 142 | 143 | def Value::dbg(&this): string { 144 | let buf = JSON::serialize(this) 145 | return buf.str() 146 | } 147 | 148 | def Value::free(&this) { 149 | match .type { 150 | String => .u.as_str.free() 151 | List => { 152 | let vec = .u.as_list 153 | for let i = 0; i < vec.size; i += 1 { 154 | Value::free(vec.at(i)) 155 | } 156 | Vector::free(.u.as_list) 157 | } 158 | Dictionary => { 159 | let map = .u.as_dict 160 | for let iter = map.iter(); iter.cur?; iter.next() { 161 | free(iter.cur.key) 162 | Value::free(iter.cur.value) 163 | } 164 | Map::free(.u.as_dict) 165 | } 166 | else => {} 167 | } 168 | free(this) 169 | } -------------------------------------------------------------------------------- /lib/vec.ae: -------------------------------------------------------------------------------- 1 | // A type that represents a vector in 3D space 2 | 3 | use "lib/math.ae" 4 | 5 | struct Vec { 6 | x: f32 7 | y: f32 8 | z: f32 9 | } 10 | 11 | def Vec::print(this) { 12 | println("%f %f %f\n", .x, .y, .z) 13 | } 14 | 15 | def Vec::add(this, other: Vec): Vec => Vec(.x + other.x, .y + other.y, .z + other.z) 16 | def Vec::addf(this, val: f32): Vec => Vec(.x + val, .y + val, .z + val) 17 | 18 | def Vec::sub(this, other: Vec): Vec => Vec(.x - other.x, .y - other.y, .z - other.z) 19 | def Vec::subf(this, val: f32): Vec => Vec(.x - val, .y - val, .z - val) 20 | 21 | def Vec::mult(this, other: Vec): Vec => Vec(.x * other.x, .y * other.y, .z * other.z) 22 | def Vec::multf(this, val: f32): Vec => Vec(.x * val, .y * val, .z * val) 23 | 24 | def Vec::div(this, other: Vec): Vec => Vec(.x / other.x, .y / other.y, .z / other.z) 25 | def Vec::divf(this, val: f32): Vec => Vec(.x / val, .y / val, .z / val) 26 | 27 | def Vec::dot(this, other: Vec): f32 => .x * other.x + .y * other.y + .z * other.z 28 | 29 | def Vec::cross(this, other: Vec): Vec { 30 | return Vec( 31 | .y * other.z - .z * other.y, 32 | .z * other.x - .x * other.z, 33 | .x * other.y - .y * other.x, 34 | ) 35 | } 36 | 37 | 38 | def Vec::length(this): f32 => sqrt(.x * .x + .y * .y + .z * .z) 39 | def Vec::length_sq(this): f32 => .x * .x + .y * .y + .z * .z 40 | def Vec::normalized(this): Vec => .divf(.length()) 41 | -------------------------------------------------------------------------------- /lib/vector.ae: -------------------------------------------------------------------------------- 1 | // Type representing a dynamic list of objects. 2 | // 3 | // Due to no generics being available, this is NOT type-safe, but rather 4 | // stored a list of `untyped_ptr`. When pushing / popping an object, 5 | // manually cast to the correct type. 6 | 7 | struct Vector { 8 | size: i32 9 | capacity: i32 10 | data: &untyped_ptr 11 | } 12 | 13 | def Vector::new_sized(capacity: i32): &Vector { 14 | let vec = calloc(1, sizeof(Vector)) as &Vector 15 | vec.size = 0 16 | vec.capacity = capacity 17 | vec.data = calloc(vec.capacity, sizeof(untyped_ptr)) as &untyped_ptr 18 | return vec 19 | } 20 | 21 | def Vector::new(): &Vector => Vector::new_sized(16) 22 | 23 | def Vector::resize(&this, new_capacity: i32) { 24 | .capacity = new_capacity 25 | .data = realloc(.data , 26 | .capacity * sizeof(untyped_ptr)) as &untyped_ptr 27 | } 28 | 29 | def Vector::push(&this, val: untyped_ptr) { 30 | if .size == .capacity { 31 | .resize(.capacity * 2) 32 | } 33 | .data[.size] = val 34 | .size += 1 35 | } 36 | 37 | def Vector::push_front(&this, val: untyped_ptr) { 38 | if .size == .capacity { 39 | .resize(.capacity * 2) 40 | } 41 | for let i = .size; i > 0; i -= 1 { 42 | .data[i] = .data[i - 1] 43 | } 44 | .data[0] = val 45 | .size += 1 46 | } 47 | 48 | def Vector::pop(&this): untyped_ptr { 49 | if .size == 0 then panic("pop on empty vector") 50 | .size -= 1 51 | return .data[.size] 52 | } 53 | 54 | def Vector::back(&this): untyped_ptr { 55 | if .size == 0 then panic("back on empty vector") 56 | return .data[.size - 1] 57 | } 58 | 59 | def Vector::at(&this, i: i32): untyped_ptr { 60 | if i < 0 or i >= .size { 61 | panic("at out of bounds in vector") 62 | } 63 | return .data[i] 64 | } 65 | 66 | def Vector::empty(&this): bool => .size == 0 67 | 68 | def Vector::free(&this) { 69 | free(.data) 70 | free(this) 71 | } -------------------------------------------------------------------------------- /meta/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p build 4 | 5 | set -e 6 | 7 | echo "[+] Building stage 1 compiler" 8 | gcc -o build/stage1 bootstrap/stage0.c 9 | echo "[+] Building stage 2 compiler" 10 | ./build/stage1 compiler/main.ae -o build/stage2 11 | echo "[+] Building stage 3 compiler" 12 | ./build/stage2 compiler/main.ae -o build/stage3 13 | echo "[+] Comparing stage 2 and 3" 14 | 15 | if diff build/stage2.c build/stage3.c; then 16 | ./build/stage3 -s -d compiler/main.ae -o ./build/aecor 17 | mv build/aecor bootstrap/aecor 18 | echo "[+] Bootstrap successful: Use ./bootstrap/aecor" 19 | else 20 | echo "[-] Error: Stage 2 and 3 are different" 21 | exit 1 22 | fi 23 | -------------------------------------------------------------------------------- /meta/gen_bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | initial=$1 4 | if [ -z "$initial" ]; then 5 | initial=./bootstrap/aecor 6 | fi 7 | 8 | mkdir -p build 9 | 10 | set -e 11 | 12 | echo "[+] Testing 3-stage bootstrap for validity" 13 | $initial compiler/main.ae -o build/stage1 14 | ./build/stage1 compiler/main.ae -o build/stage2 15 | ./build/stage2 compiler/main.ae -o build/stage3 16 | if diff build/stage2.c build/stage3.c; then 17 | echo "[+] Verification successful!" 18 | echo 19 | else 20 | echo "[-] Error: Stage 2 and 3 are different, pleaes verify manually" 21 | exit 1 22 | fi 23 | 24 | echo "[+] Running test suite" 25 | if python3 meta/test.py -c ./build/stage3; then 26 | echo 27 | else 28 | echo 29 | echo "[-] Error: Test suite failed" 30 | exit 1 31 | fi 32 | 33 | 34 | read -p "Are you sure you want to replace bootstrap/stage0.c? [y/N] " confirm 35 | if [[ $confirm =~ ^[Yy]$ ]]; then 36 | echo "[+] cp build/stage3.c bootstrap/stage0.c" 37 | cp build/stage3.c bootstrap/stage0.c 38 | echo "[+] Creating debug version into /bootstrap/aecor" 39 | ./build/stage3 -s -d compiler/main.ae -o ./build/aecor 40 | cp build/aecor bootstrap/aecor 41 | echo "Done." 42 | else 43 | echo "[-] Aborting" 44 | exit 1 45 | fi 46 | -------------------------------------------------------------------------------- /meta/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import shutil 3 | from subprocess import run, PIPE 4 | import argparse 5 | from ast import literal_eval 6 | from dataclasses import dataclass 7 | from enum import Enum 8 | from os import system, makedirs 9 | from pathlib import Path 10 | from sys import argv 11 | from typing import Union, Optional, Tuple 12 | import multiprocessing 13 | import textwrap 14 | 15 | class Result(Enum): 16 | EXIT_WITH_CODE = 1 17 | EXIT_WITH_OUTPUT = 2 18 | COMPILE_FAIL = 3 19 | COMPILE_SUCCESS = 4 20 | SKIP_SILENTLY = 5 21 | SKIP_REPORT = 6 22 | 23 | 24 | @dataclass(frozen=True) 25 | class Expected: 26 | type: Result 27 | value: Union[int, str, None] 28 | 29 | 30 | def get_expected(filename) -> Optional[Expected]: 31 | with open(filename) as file: 32 | for line in file: 33 | if not line.startswith("///"): 34 | break 35 | 36 | line = line[3:].strip() 37 | 38 | # Commands with no arguments 39 | if line == "skip": 40 | return Expected(Result.SKIP_SILENTLY, None) 41 | if line == "compile": 42 | return Expected(Result.COMPILE_SUCCESS, None) 43 | if line == "": 44 | continue 45 | 46 | if ":" not in line: 47 | print(f'[-] Invalid parameters in {filename}: "{line}"') 48 | break 49 | 50 | # Commands with arguments 51 | name, value = map(str.strip, line.split(":", 1)) 52 | 53 | if name == "exit": 54 | return Expected(Result.EXIT_WITH_CODE, int(value)) 55 | if name == "out": 56 | return Expected(Result.EXIT_WITH_OUTPUT, value) 57 | if name == "fail": 58 | return Expected(Result.COMPILE_FAIL, value) 59 | 60 | print(f'[-] Invalid parameter in {filename}: {line}') 61 | break 62 | 63 | return Expected(Result.SKIP_REPORT, None) 64 | 65 | def handle_test(compiler: str, num: int, path: Path, expected: Expected) -> Tuple[bool, str, Path]: 66 | exec_name = f'./build/tests/{path.stem}-{num}' 67 | process = run( 68 | [compiler, str(path), '-o', exec_name], 69 | stdout=PIPE, 70 | stderr=PIPE 71 | ) 72 | 73 | if expected.type == Result.COMPILE_FAIL: 74 | if process.returncode == 0: 75 | return False, "Expected compilation failure, but succeeded", path 76 | 77 | error = process.stdout.decode("utf-8").strip() 78 | expected_error = expected.value 79 | 80 | if expected_error in error: 81 | return True, "(Success)", path 82 | else: 83 | try: 84 | remaining = error.split("Error: ")[1] 85 | except IndexError: 86 | remaining = error 87 | return False, f"Did not find expected error message\n expected: {expected_error}\n got: '{remaining}'", path 88 | 89 | elif process.returncode != 0: 90 | stdout = textwrap.indent(process.stdout.decode("utf-8"), " "*10).strip() 91 | stderr = textwrap.indent(process.stderr.decode("utf-8"), " "*10).strip() 92 | return False, f"Compilation failed:\n code: {process.returncode}\n stdout: {stdout}\n stderr: {stderr}", path 93 | 94 | elif expected.type == Result.COMPILE_SUCCESS: 95 | return True, "(Success)", path 96 | 97 | process = run([exec_name], stdout=PIPE, stderr=PIPE) 98 | 99 | if process.returncode != 0 and expected.type != Result.EXIT_WITH_CODE: 100 | return False, f"Expected exit code 0, but got {process.returncode}", path 101 | 102 | if expected.type == Result.EXIT_WITH_CODE: 103 | if process.returncode != expected.value: 104 | return False, "Expected exit code {expected.value}, but got {process.returncode}", path 105 | 106 | if expected.type == Result.EXIT_WITH_OUTPUT: 107 | output = process.stdout.decode('utf-8').strip() 108 | expected_out = literal_eval(expected.value).strip() 109 | if output != expected_out: 110 | return False, f'Incorrect output produced\n expected: {repr(expected_out)}\n got: {repr(output)}', path 111 | 112 | return True, "(Success)", path 113 | 114 | def pool_helper(args): 115 | return handle_test(*args) 116 | 117 | def main(): 118 | parser = argparse.ArgumentParser(description="Runs aecor test suite") 119 | parser.add_argument( 120 | "-c", 121 | "--compiler", 122 | required=True, 123 | help="Runs the self-hosted version" 124 | ) 125 | parser.add_argument( 126 | "files", 127 | nargs="?", 128 | default=["tests"], 129 | help="Files / folders to run" 130 | ) 131 | args = parser.parse_args() 132 | arg_files = args.files if isinstance(args.files, list) else [args.files] 133 | test_paths = [Path(pth) for pth in arg_files] 134 | 135 | tests_to_run = [] 136 | for path in test_paths: 137 | files = [] 138 | 139 | if path.is_dir(): 140 | for path_ in path.glob('**/*'): 141 | if path_.is_file(): 142 | files.append(path_) 143 | else: 144 | files.append(path) 145 | 146 | for file in files: 147 | expected = get_expected(file) 148 | if expected.type == Result.SKIP_SILENTLY: 149 | continue 150 | if expected.type == Result.SKIP_REPORT: 151 | print(f'[-] Skipping {file}') 152 | continue 153 | tests_to_run.append((file, expected)) 154 | 155 | num_passed = 0 156 | num_failed = 0 157 | num_total = len(tests_to_run) 158 | 159 | arguments = [ 160 | (args.compiler, num, test_path, expected) 161 | for num, (test_path, expected) in enumerate(tests_to_run) 162 | ] 163 | 164 | # Clear out existing test directories 165 | shutil.rmtree("build/tests", ignore_errors=True) 166 | makedirs("build/tests", exist_ok=True) 167 | 168 | with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: 169 | for passed, message, path in pool.imap_unordered(pool_helper, arguments): 170 | print(f" \33[2K[\033[92m{num_passed:3d}\033[0m", end="") 171 | print(f"/\033[91m{num_failed:3d}\033[0m]", end="") 172 | print(f" Running tests, finished {num_passed+num_failed} / {num_total}\r", end="", flush=True) 173 | if passed: 174 | num_passed += 1 175 | else: 176 | num_failed += 1 177 | print(f"\33[2K\033[91m[-] Failed {path}\033[0m") 178 | print(f" - {message}", flush=True) 179 | 180 | print("\33[2K") 181 | print(f"Tests passed: \033[92m{num_passed}\033[0m") 182 | print(f"Tests failed: \033[91m{num_failed}\033[0m") 183 | 184 | if num_failed > 0: 185 | exit(1) 186 | 187 | 188 | if __name__ == "__main__": 189 | main() 190 | -------------------------------------------------------------------------------- /tests/array_type.ae: -------------------------------------------------------------------------------- 1 | /// out: "234" 2 | 3 | def add_one(a: &i32) { 4 | a[0] += 1 5 | a[1] += 1 6 | a[2] += 1 7 | } 8 | 9 | def main() { 10 | let y: [[i32; 3]; 3] 11 | y[1][0] = 1 12 | y[1][1] = 2 13 | y[1][2] = 3 14 | add_one(y[1]) 15 | 16 | print("%d%d%d", y[1][0], y[1][1], y[1][2]) 17 | } -------------------------------------------------------------------------------- /tests/arrow_function.ae: -------------------------------------------------------------------------------- 1 | /// out: "2" 2 | 3 | def add_one(x: i32): i32 => x + 1 4 | 5 | struct Foo { 6 | x: i32 7 | } 8 | 9 | def Foo::lol(this): i32 => match .x { 10 | 0 => 1 11 | else => 4 12 | } 13 | 14 | def main() { 15 | let x = Foo(0) 16 | println("%d", add_one(x.lol())) 17 | } 18 | -------------------------------------------------------------------------------- /tests/assign.ae: -------------------------------------------------------------------------------- 1 | /// out: "The value of x is 6" 2 | 3 | def main() { 4 | let x: i32 = 5 5 | x = x + 1 6 | println("The value of x is %d", x) 7 | } -------------------------------------------------------------------------------- /tests/bad/arith_binop_not_num.ae: -------------------------------------------------------------------------------- 1 | /// fail: Operator requires numeric types 2 | 3 | def main() { 4 | let x = true + 5 5 | } -------------------------------------------------------------------------------- /tests/bad/assign_bad_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Variable type does not match assignment type 2 | 3 | def main() { 4 | let x: bool 5 | x = 5 6 | } -------------------------------------------------------------------------------- /tests/bad/assign_method_to_var.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot assign methods to variables 2 | 3 | struct Foo {} 4 | 5 | def Foo::get(this) {} 6 | 7 | def main() { 8 | let f: Foo 9 | let g = f.get 10 | } 11 | -------------------------------------------------------------------------------- /tests/bad/assign_to_lvalue.ae: -------------------------------------------------------------------------------- 1 | /// fail: Must be an l-value 2 | 3 | def main() { 4 | 5 + 1 = 4 5 | } -------------------------------------------------------------------------------- /tests/bad/break_outside.ae: -------------------------------------------------------------------------------- 1 | /// fail: Break statement outside of loop 2 | 3 | def main() { 4 | break 5 | } 6 | -------------------------------------------------------------------------------- /tests/bad/call_non_function.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot call a non-function type 2 | 3 | def main() { 4 | let x = 5 5 | println("%d", x()) 6 | } 7 | -------------------------------------------------------------------------------- /tests/bad/const_expr_unsupported.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unsupported operator in constant expression 2 | 3 | const foo = "Hello" 4 | const bar = &foo -------------------------------------------------------------------------------- /tests/bad/const_expr_var.ae: -------------------------------------------------------------------------------- 1 | /// fail: No constant value found with this name 2 | let x = 0 3 | const y = x + 1 4 | 5 | def main() => 0 -------------------------------------------------------------------------------- /tests/bad/continue_outside.ae: -------------------------------------------------------------------------------- 1 | /// fail: Continue statement outside of loop 2 | 3 | def main() { 4 | continue 5 | } 6 | -------------------------------------------------------------------------------- /tests/bad/deref_non_ptr.ae: -------------------------------------------------------------------------------- 1 | /// fail: Expression must be a pointer-type 2 | 3 | def main() { 4 | let x = 5 5 | let y = *x 6 | } -------------------------------------------------------------------------------- /tests/bad/enum_call_value.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot call a non-function type 2 | 3 | enum Variable { 4 | Integer 5 | IntLiteral 6 | Float 7 | String 8 | } 9 | 10 | def main() { 11 | let x = Variable::Integer() 12 | } -------------------------------------------------------------------------------- /tests/bad/enum_get_member_dot.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type 'Variable' has no member with this name 2 | 3 | enum Variable { 4 | Integer 5 | IntLiteral 6 | Float 7 | String 8 | } 9 | 10 | def main() { 11 | let x = Variable::Integer 12 | let y = x.Float 13 | } -------------------------------------------------------------------------------- /tests/bad/enum_method_with_same_name.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type already has a field with this name 2 | 3 | enum Variable { 4 | Integer 5 | IntLiteral 6 | Float 7 | String 8 | } 9 | 10 | def Variable::Integer() {} 11 | -------------------------------------------------------------------------------- /tests/bad/expr-statements/block_multiple_yields.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot yield multiple times in a block 2 | 3 | def main() { 4 | let x = match 5 { 5 | 0 => { 6 | yield "hello" 7 | yield "bye" 8 | } 9 | else => "ok" 10 | } 11 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/if_missing_else.ae: -------------------------------------------------------------------------------- 1 | /// fail: Expression-if must have an 'else' branch 2 | 3 | def main() { 4 | let x = if 5 < 10 { 5 | yield "hello" 6 | } 7 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/if_missing_yield.ae: -------------------------------------------------------------------------------- 1 | /// fail: Must yield a value in this branch 2 | 3 | def main() { 4 | let x = if 3 > 5 { 5 | yield 10 6 | } else { 7 | // nope 8 | } 9 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/match_empty.ae: -------------------------------------------------------------------------------- 1 | /// fail: Expression-match must yield a value 2 | 3 | enum Hello {} 4 | 5 | def main() { 6 | let x: Hello 7 | let y = match x {} 8 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/match_missing_yield.ae: -------------------------------------------------------------------------------- 1 | /// fail: Must yield a value in this branch 2 | 3 | def main() { 4 | let x = match 5 { 5 | 0 => let y = "hello" 6 | else => let z = "bye" 7 | } 8 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/nested_block_yield.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot yield in this context 2 | 3 | def main() { 4 | let x = if 5 < 10 { 5 | { 6 | yield "bye" 7 | } 8 | } else { 9 | yield "hi" 10 | } 11 | } -------------------------------------------------------------------------------- /tests/bad/expr-statements/yield_type_mismatch.ae: -------------------------------------------------------------------------------- 1 | /// fail: Yield type doesn't match previous branches 2 | 3 | def main() { 4 | let x = match 5 { 5 | 0 => "hello" 6 | 1 => 4 7 | else => true 8 | } 9 | } -------------------------------------------------------------------------------- /tests/bad/field_type_undef.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type of field is undefined 2 | 3 | struct Anything { 4 | a: MadeUp 5 | } 6 | -------------------------------------------------------------------------------- /tests/bad/func_bad_arg_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Argument type does not match function parameter type 2 | 3 | def foo(x: i32) { } 4 | 5 | def main() { 6 | foo(true) 7 | } -------------------------------------------------------------------------------- /tests/bad/func_bad_num_args.ae: -------------------------------------------------------------------------------- 1 | /// fail: Number of arguments does not match function signature 2 | 3 | def foo(a: i32, b: i32): i32 { 4 | return a + b 5 | } 6 | 7 | def main() { 8 | foo(1, 2, 3) 9 | } -------------------------------------------------------------------------------- /tests/bad/func_invalid_param.ae: -------------------------------------------------------------------------------- 1 | /// fail: Invalid parameter type 2 | 3 | def foo(x: Foo) { } -------------------------------------------------------------------------------- /tests/bad/func_invalid_return.ae: -------------------------------------------------------------------------------- 1 | /// fail: Invalid return type 2 | 3 | def foo(x: i32): Foo { } -------------------------------------------------------------------------------- /tests/bad/func_not_found.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unknown Identifier 2 | 3 | def main() { 4 | foo() 5 | } -------------------------------------------------------------------------------- /tests/bad/func_redef.ae: -------------------------------------------------------------------------------- 1 | /// fail: Function is already defined 2 | 3 | def foo(): i32 { } 4 | def foo(): bool { } -------------------------------------------------------------------------------- /tests/bad/func_ret_from_void.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot return 'i32' in void function 2 | 3 | def foo() { 4 | return 5 5 | } 6 | -------------------------------------------------------------------------------- /tests/bad/func_ret_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Return type 'bool' is incorrect 2 | 3 | def main() : i32 { 4 | let x: i32 = 5 5 | let y: i32 = 7 6 | let z: bool = true 7 | { 8 | let p: i32 = 3 9 | } 10 | return z 11 | } 12 | -------------------------------------------------------------------------------- /tests/bad/if_not_bool.ae: -------------------------------------------------------------------------------- 1 | /// fail: Condition must be a boolean 2 | 3 | def main() { 4 | if (0) { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/bad/incorrect_labelled_param.ae: -------------------------------------------------------------------------------- 1 | /// fail: Label on parameter does not match parameter name 2 | 3 | def foo(val: i32, does_exist: bool): i32 { 4 | return val 5 | } 6 | 7 | def main() { 8 | let x = foo(1, true) 9 | let y = foo(2, incorrect: false) 10 | } -------------------------------------------------------------------------------- /tests/bad/invalid_yield.ae: -------------------------------------------------------------------------------- 1 | /// fail: Cannot yield in this context 2 | 3 | def main() { 4 | yield 5 5 | } -------------------------------------------------------------------------------- /tests/bad/lexer1.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unexpected token 2 | 3 | def main() { 4 | let x: i32 = bool 5 | } 6 | -------------------------------------------------------------------------------- /tests/bad/logical_ops_not_bool.ae: -------------------------------------------------------------------------------- 1 | /// fail: Operands must be boolean 2 | 3 | def main() { 4 | let x = true or 5 5 | } -------------------------------------------------------------------------------- /tests/bad/match_duplicate.ae: -------------------------------------------------------------------------------- 1 | /// fail: Duplicate condition name in match 2 | 3 | enum People { 4 | LP 5 | Charlie 6 | KJ 7 | Coffee 8 | } 9 | 10 | def main() { 11 | let x = People::LP 12 | match x { 13 | People::LP => {} 14 | People::KJ => {} 15 | People::Coffee => {} 16 | People::Coffee => {} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/bad/match_not_enum.ae: -------------------------------------------------------------------------------- 1 | /// fail: This type cannot be matched on 2 | 3 | def main() { 4 | match 3.14159 { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /tests/bad/match_not_exhaustive.ae: -------------------------------------------------------------------------------- 1 | /// fail: Match does not cover all cases 2 | 3 | enum People { 4 | LP 5 | Charlie 6 | KJ 7 | Coffee 8 | } 9 | 10 | def main() { 11 | let x = People::LP 12 | match x { 13 | People::LP => {} 14 | People::KJ => {} 15 | People::Coffee => {} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/bad/method_ampersand_without_this.ae: -------------------------------------------------------------------------------- 1 | /// fail: Expected 'this' over here 2 | 3 | struct Foo {} 4 | 5 | // This isn't allowed. 6 | def Foo::foo(&x: i32) {} -------------------------------------------------------------------------------- /tests/bad/methods_invalid_struct.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type for method does not exist 2 | 3 | def Foo::bar() {} -------------------------------------------------------------------------------- /tests/bad/methods_redef.ae: -------------------------------------------------------------------------------- 1 | /// fail: Method is already defined for this type 2 | 3 | struct Foo {} 4 | def Foo::bar(this) {} 5 | def Foo::bar(this) {} -------------------------------------------------------------------------------- /tests/bad/missing_line_end.ae: -------------------------------------------------------------------------------- 1 | /// fail: Expected Comma or newline 2 | 3 | struct Foo { 4 | a: i32 b: i32 5 | } -------------------------------------------------------------------------------- /tests/bad/no_return_if.ae: -------------------------------------------------------------------------------- 1 | /// fail: Function does not always return 2 | 3 | def foo(): i32 { 4 | if true { 5 | return 5 6 | } else { 7 | print("nope") 8 | } 9 | } -------------------------------------------------------------------------------- /tests/bad/no_return_regular.ae: -------------------------------------------------------------------------------- 1 | /// fail: Function does not always return 2 | 3 | def foo(): i32 { 4 | print("Hello, World!") 5 | } -------------------------------------------------------------------------------- /tests/bad/no_return_switch.ae: -------------------------------------------------------------------------------- 1 | /// fail: Function does not always return 2 | 3 | def foo(): string { 4 | match 5 { 5 | 1 => return "hello" 6 | 2 => return "world" 7 | 3 => {} 8 | else => return "nope" 9 | } 10 | } -------------------------------------------------------------------------------- /tests/bad/no_static_member.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type 'Foo' has no member with this name 2 | 3 | struct Foo {} 4 | 5 | def main() { 6 | Foo::foo() 7 | Bar::foo() 8 | } -------------------------------------------------------------------------------- /tests/bad/question_not_ptr.ae: -------------------------------------------------------------------------------- 1 | /// fail: Can only use ? on pointer types 2 | 3 | def main() { 4 | let x = 50 5 | if x? {} 6 | } -------------------------------------------------------------------------------- /tests/bad/sizeof_bad_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Invalid type 2 | 3 | def main() { 4 | let x = sizeof(Foo) 5 | } -------------------------------------------------------------------------------- /tests/bad/static_member_invalid_struct.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unknown struct with this name 2 | 3 | def main() { 4 | Bar::foo() 5 | } -------------------------------------------------------------------------------- /tests/bad/static_method_on_instance.ae: -------------------------------------------------------------------------------- 1 | /// fail: Member access requires a non-static method 2 | 3 | struct Foo {} 4 | def Foo::foo(): i32 { 5 | return 0 6 | } 7 | 8 | def main() { 9 | let f: Foo 10 | println("%d", f.foo()) 11 | } -------------------------------------------------------------------------------- /tests/bad/struct_method_redef_field.ae: -------------------------------------------------------------------------------- 1 | /// fail: Type already has a field with this name 2 | 3 | struct Foo { 4 | len: i32 5 | } 6 | 7 | def Foo::len(this) {} 8 | -------------------------------------------------------------------------------- /tests/bad/struct_redef.ae: -------------------------------------------------------------------------------- 1 | /// fail: Struct has already been defined 2 | 3 | struct Foo { 4 | a: i32 5 | } 6 | 7 | struct Foo { 8 | b: bool 9 | } -------------------------------------------------------------------------------- /tests/bad/typed_literals.ae: -------------------------------------------------------------------------------- 1 | /// fail: Invalid type 2 | 3 | def main() { 4 | let x = 125u128 5 | } -------------------------------------------------------------------------------- /tests/bad/var1.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unknown Identifier 2 | 3 | def main() : i32 { 4 | let x: i32 = 5 5 | let y: i32 = 7 6 | let z: bool = true 7 | { 8 | let p: i32 = 3 9 | } 10 | return p 11 | } 12 | -------------------------------------------------------------------------------- /tests/bad/var_cannot_infer.ae: -------------------------------------------------------------------------------- 1 | /// fail: Variable type cannot be inferred, specify explicitly 2 | 3 | def main() { 4 | let x 5 | } -------------------------------------------------------------------------------- /tests/bad/var_invalid_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Invalid variable type 2 | 3 | def main() { 4 | let x: Foo 5 | } -------------------------------------------------------------------------------- /tests/bad/var_not_found.ae: -------------------------------------------------------------------------------- 1 | /// fail: Unknown Identifier 2 | 3 | def main() { 4 | return x 5 | } -------------------------------------------------------------------------------- /tests/bad/var_redef.ae: -------------------------------------------------------------------------------- 1 | /// fail: Variable is already defined in scope 2 | 3 | def main() { 4 | let x = 0 5 | let x = 7 6 | } -------------------------------------------------------------------------------- /tests/bad/var_wrong_type.ae: -------------------------------------------------------------------------------- 1 | /// fail: Variable type does not match initializer type 2 | 3 | def main() { 4 | let x: i32 = true 5 | } -------------------------------------------------------------------------------- /tests/bad/while_not_bool.ae: -------------------------------------------------------------------------------- 1 | /// fail: Condition must be boolean 2 | 3 | def main() { 4 | while (0) { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/binop1.ae: -------------------------------------------------------------------------------- 1 | /// exit: 143 2 | 3 | def main() : i32 { 4 | return 11 * (10 + 3) 5 | } -------------------------------------------------------------------------------- /tests/binop2.ae: -------------------------------------------------------------------------------- 1 | /// exit: 113 2 | 3 | def main() : i32 { 4 | return 11 * 10 + 3 5 | } -------------------------------------------------------------------------------- /tests/binop_order.ae: -------------------------------------------------------------------------------- 1 | /// out: "0 2" 2 | 3 | def main() { 4 | let x = 5 - 2 - 3 5 | let y = 8 / 2 / 2 6 | 7 | println("%d %d", x, y) 8 | } -------------------------------------------------------------------------------- /tests/binops.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | def main() { 4 | let x = 1 5 | x += 1 6 | x = x + 1 7 | 8 | x *= 2 9 | x = x * 2 10 | 11 | x /= 2 12 | x = x / 2 13 | 14 | x -= 1 15 | x = x - 1 16 | 17 | let a0 = 0 == 0 18 | let a1 = 0 != 0 19 | let a2 = 0 < 0 20 | let a3 = 0 > 0 21 | let a4 = 0 <= 0 22 | let a5 = 0 >= 0 23 | } 24 | -------------------------------------------------------------------------------- /tests/break.ae: -------------------------------------------------------------------------------- 1 | /// out: "5" 2 | 3 | def main() { 4 | let n = 0 5 | 6 | while true { 7 | if n == 5 { 8 | break 9 | } else { 10 | n += 1 11 | } 12 | } 13 | 14 | println("%d", n) 15 | } 16 | -------------------------------------------------------------------------------- /tests/cast.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | def foo(): bool { 4 | return true 5 | } 6 | 7 | def main() { 8 | let x = (0 as bool) or true 9 | let y = (foo() as i32) + 5 10 | } -------------------------------------------------------------------------------- /tests/chars.ae: -------------------------------------------------------------------------------- 1 | /// out: "a\nb" 2 | 3 | def main() { 4 | let a: char = 'a' 5 | let b = 'b' 6 | let newline = '\n' 7 | 8 | println("%c%c%c", a, newline, b) 9 | } 10 | -------------------------------------------------------------------------------- /tests/constants.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | const X = 1 4 | const Y = X * 2 5 | const Z = Y * 4 6 | 7 | struct Foo { 8 | values: [u8; Z] 9 | } 10 | 11 | def main() => 0 -------------------------------------------------------------------------------- /tests/constructors.ae: -------------------------------------------------------------------------------- 1 | /// out: "6" 2 | 3 | struct Vec { 4 | x: i32 5 | y: f32 6 | } 7 | 8 | def main() { 9 | let x = Vec(x: 1, y: 2.0) 10 | let y = Vec(2, 1.0) 11 | let z = x.x as f32 + y.y 12 | let w = x.y + y.x as f32 13 | println("%d", (z + w) as i32) 14 | } -------------------------------------------------------------------------------- /tests/continue.ae: -------------------------------------------------------------------------------- 1 | /// out: "1 3 5 7 9 " 2 | 3 | def main() { 4 | for let i = 0; i < 11; i += 1 { 5 | if i % 2 == 0 { 6 | continue 7 | } 8 | 9 | print("%d ", i) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/defer.ae: -------------------------------------------------------------------------------- 1 | /// out: "1\n2\n3\n4\n5" 2 | 3 | def main() { 4 | defer println("%d", 5) 5 | println("%d", 1) 6 | { 7 | defer println("%d", 3) 8 | println("%d", 2) 9 | } 10 | println("%d", 4) 11 | } -------------------------------------------------------------------------------- /tests/empty_ret.ae: -------------------------------------------------------------------------------- 1 | /// out: "lmao" 2 | 3 | def foo(x: i32) { 4 | if x < 10 { 5 | print("lmao") 6 | return 7 | } 8 | print("oops") 9 | } 10 | 11 | def main() { 12 | foo(5) 13 | } -------------------------------------------------------------------------------- /tests/enum.ae: -------------------------------------------------------------------------------- 1 | /// out: "PASS" 2 | 3 | enum Variable { 4 | Integer 5 | IntLiteral 6 | Float 7 | String 8 | } 9 | 10 | def main() { 11 | let x = Variable::Integer 12 | let y = Variable::IntLiteral 13 | 14 | if x == y { 15 | println("FAIL") 16 | } else { 17 | println("PASS") 18 | } 19 | } -------------------------------------------------------------------------------- /tests/enum_auto_dbg.ae: -------------------------------------------------------------------------------- 1 | /// out: "Lmao Goodbye" 2 | 3 | enum Foo { 4 | Lmao 5 | Hello 6 | Goodbye 7 | } 8 | 9 | def main() { 10 | let x = Foo::Lmao 11 | let y = Foo::Goodbye 12 | println("%s %s", x, y) 13 | } -------------------------------------------------------------------------------- /tests/escaped_backslash_fstr.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def main() { 4 | println(`\\{5}`) 5 | } -------------------------------------------------------------------------------- /tests/expr-statements.ae: -------------------------------------------------------------------------------- 1 | /// out: "pass" 2 | 3 | def main() { 4 | let a = 23 5 | println("%s", match a % 20 { 6 | 0 => "zero" // Implicit yield 7 | 1 => { 8 | yield "one" 9 | } 10 | 2 => { 11 | // This is not yielded, so types don't matter 12 | match a % 10 { 13 | 0 => "ten" 14 | 1 => 7 15 | else => true 16 | } 17 | 18 | yield match a % 10 { 19 | 0 => "test" 20 | else => "fail" 21 | } 22 | } 23 | 3 => match a * 3 { 24 | 0 => "zero" 25 | 69 => { 26 | yield match 0 { 27 | 99 => "pass" 28 | else => if 5 > 10 { 29 | yield "fail" 30 | } else if 5 < 10 31 | "pass" // Implicit yield 32 | else 33 | "bruh" 34 | } 35 | } 36 | else => "huh" 37 | } 38 | else => { 39 | yield "out of luck" 40 | } 41 | }) 42 | } -------------------------------------------------------------------------------- /tests/extern.ae: -------------------------------------------------------------------------------- 1 | /// out: "Hello, world!" 2 | 3 | struct FILE extern 4 | 5 | def file_open(s: string, p: string): &FILE extern("fopen") 6 | def file_write(s: string, a: i32, b: i32, f: &FILE): i32 extern("fwrite") 7 | 8 | def main() { 9 | let f = file_open("/dev/stdout", "w"); 10 | file_write("Hello, world!", 1, 13, f); 11 | return 0 12 | } 13 | -------------------------------------------------------------------------------- /tests/file_io.ae: -------------------------------------------------------------------------------- 1 | /// out: "Hello, world!" 2 | 3 | def main() { 4 | { 5 | let file = File::open("test.txt", "w") 6 | defer file.close() 7 | 8 | file.write("Hello, world!" , 13) 9 | } 10 | { 11 | let file = File::open("test.txt", "r") 12 | defer file.close() 13 | 14 | let mem = malloc(20) 15 | file.read(mem, 13) 16 | println("%s", mem as string) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/float.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | def main() { 4 | let y = 1 + 2 5 | let z = 1.0 + 2.0 6 | 7 | let a = 1 < 2 8 | let b = 1.0 > 2.0 9 | 10 | let m = -1 11 | let n = -1.0 12 | } -------------------------------------------------------------------------------- /tests/for.ae: -------------------------------------------------------------------------------- 1 | /// out: "1\n2\n3\n4\n5\n6\n7\n8\n9" 2 | 3 | def main() { 4 | let i: i32 5 | for i = 1; i < 10; i = i + 1 { 6 | println("%d", i) 7 | } 8 | } -------------------------------------------------------------------------------- /tests/format_str.ae: -------------------------------------------------------------------------------- 1 | /// out: 'x is "Hello", `y` = {9}' 2 | 3 | def main() { 4 | let x = "Hello" 5 | let q = `x is "{x}", \`y\` = \{{5 + 4}\}` 6 | println("%s", q) 7 | } -------------------------------------------------------------------------------- /tests/format_str_specs.ae: -------------------------------------------------------------------------------- 1 | /// out: "hello 21 0021 33\nhello 21 0021 33" 2 | 3 | struct Foo {} 4 | def Foo::bar(): i32 => 33 5 | 6 | def main() { 7 | let x = `hello {Foo::bar() :02x} {33: 04X} {33}` 8 | println("%s", x) 9 | println(`hello {Foo::bar() :02x} {33: 04X} {33}`) 10 | } -------------------------------------------------------------------------------- /tests/func_pointers.ae: -------------------------------------------------------------------------------- 1 | /// out: "0\n1" 2 | 3 | struct Foo { 4 | func: fn(): i32 5 | } 6 | 7 | def a0(): i32 { return 0; } 8 | def a1(): i32 { return 1; } 9 | 10 | def main() { 11 | let x: Foo 12 | x.func = a0 13 | println("%d", x.func()) 14 | x.func = a1 15 | println("%d", x.func()) 16 | } -------------------------------------------------------------------------------- /tests/funccall.ae: -------------------------------------------------------------------------------- 1 | /// exit: 3 2 | 3 | def sum(a: i32, b: i32): i32 { 4 | return a + b 5 | } 6 | 7 | def main() : i32 { 8 | return sum(1, 2) 9 | } -------------------------------------------------------------------------------- /tests/funcorder.ae: -------------------------------------------------------------------------------- 1 | /// out: "3" 2 | def main() { 3 | println("%d", add(1, 2)) 4 | } 5 | 6 | def add(a: i32, b: i32): i32 { 7 | return sub(a, -b) 8 | } 9 | 10 | def sub(a: i32, b: i32): i32 { 11 | return a - b 12 | } 13 | -------------------------------------------------------------------------------- /tests/hashmap_iter.ae: -------------------------------------------------------------------------------- 1 | /// out: "PASS" 2 | 3 | use "lib/map.ae" 4 | 5 | def main() { 6 | let map = Map::new() 7 | 8 | map.insert("key1", "value1") 9 | map.insert("key2", "value2") 10 | map.insert("key3", "value3") 11 | map.insert("key4", "value4") 12 | map.insert("key5", "value5") 13 | map.insert("key6", "value6") 14 | map.insert("key7", "value7") 15 | map.insert("key8", "value8") 16 | map.insert("key9", "value9") 17 | map.insert("key10", "value10") 18 | map.insert("key11", "value11") 19 | map.insert("key12", "value12") 20 | map.insert("key13", "value13") 21 | map.insert("key14", "value14") 22 | map.insert("key15", "value15") 23 | map.insert("key16", "value16") 24 | 25 | let count = 0 26 | for let iter = map.iter(); iter.cur?; iter.next() { 27 | count += 1 28 | } 29 | if count == map.num_buckets { 30 | println("PASS") 31 | } else { 32 | println("FAIL") 33 | } 34 | } -------------------------------------------------------------------------------- /tests/higher_order.ae: -------------------------------------------------------------------------------- 1 | /// out: "2 2 2 2" 2 | 3 | def plus_one(x: i32): i32 { return x + 1; } 4 | def times_two(x: i32): i32 { return x * 2; } 5 | def minus_one(x: i32): i32 { return x - 1; } 6 | def divide_by_two(x: i32): i32 { return x / 2; } 7 | 8 | def do_op(x: i32, op: fn(i32): i32): i32 { 9 | return op(x); 10 | } 11 | 12 | def add(x: i32, y: i32): i32 { 13 | return x + y 14 | } 15 | 16 | def retadd(none: i32): fn(i32,i32): i32 { 17 | return add 18 | } 19 | 20 | def main() { 21 | print("%d ", do_op(1, plus_one) + retadd(0)(1, -1)); 22 | print("%d ", do_op(1, times_two)) 23 | print("%d ", do_op(3, minus_one)) 24 | print("%d ", do_op(4, divide_by_two)) 25 | print("\n") 26 | } -------------------------------------------------------------------------------- /tests/if.ae: -------------------------------------------------------------------------------- 1 | /// out: "true" 2 | 3 | def main() : i32 { 4 | let f: bool = true 5 | if f { 6 | println("true") 7 | } else { 8 | println("false") 9 | } 10 | } -------------------------------------------------------------------------------- /tests/if_optional_then.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def main() { 4 | let x = if 5 < 10 then "a" else "b" 5 | if x.eq("b") then return -1 6 | 7 | if 5 < 10 { 8 | let y = "a" 9 | } else { 10 | let y = "c" 11 | } 12 | 13 | if 5 < 10 14 | x = "a" 15 | else 16 | x = "c" 17 | } -------------------------------------------------------------------------------- /tests/infer_enums.ae: -------------------------------------------------------------------------------- 1 | /// out: "pass" 2 | 3 | enum Foo { 4 | Hello 5 | World 6 | Aecor 7 | } 8 | 9 | enum Bar { 10 | World 11 | Aecor 12 | Hello 13 | } 14 | 15 | def main() { 16 | let x = Foo::Hello 17 | let y = Bar::World 18 | 19 | if x == Hello and y == World { 20 | println("pass") 21 | } else { 22 | println("fail") 23 | } 24 | } -------------------------------------------------------------------------------- /tests/inference.ae: -------------------------------------------------------------------------------- 1 | /// out: "6" 2 | 3 | enum Foo { 4 | Bar 5 | Baz 6 | Qux 7 | } 8 | 9 | struct SFoo { 10 | x: Foo 11 | } 12 | 13 | def func1(foo: Foo): u8 => match foo { 14 | Bar => 0 15 | Baz => 1 16 | Qux => 2 17 | } 18 | 19 | def func2(sfoo: SFoo): u8 => match sfoo.x { 20 | Bar => 3 21 | Baz => 4 22 | Qux => 5 23 | } 24 | 25 | 26 | def main() : i32 { 27 | let x = 5 28 | let a: u64 = 7 29 | let j = a + 3u64 30 | 31 | let k = func1(Qux) 32 | let l = func2(SFoo(x: Baz)) 33 | let m: u8 = k + l 34 | println(`{m}`) 35 | 36 | let y = 7 37 | let z = true 38 | { 39 | let p = 3 40 | } 41 | return x - x 42 | } -------------------------------------------------------------------------------- /tests/instance_method_as_static.ae: -------------------------------------------------------------------------------- 1 | /// out: "42" 2 | 3 | struct Foo { 4 | x: i32 5 | } 6 | 7 | def Foo::foo(&this): i32 { 8 | return .x 9 | } 10 | 11 | def main() { 12 | let f: Foo 13 | f.x = 42 14 | println("%d", Foo::foo(&f)) 15 | } -------------------------------------------------------------------------------- /tests/int_types.ae: -------------------------------------------------------------------------------- 1 | /// out: "pass" 2 | 3 | def main() { 4 | if sizeof(i8) != 1 println("fail") 5 | if sizeof(i16) != 2 println("fail") 6 | if sizeof(i32) != 4 println("fail") 7 | if sizeof(i64) != 8 println("fail") 8 | 9 | if sizeof(u8) != 1 println("fail") 10 | if sizeof(u16) != 2 println("fail") 11 | if sizeof(u32) != 4 println("fail") 12 | if sizeof(u64) != 8 println("fail") 13 | 14 | println("pass") 15 | } -------------------------------------------------------------------------------- /tests/json_test.ae: -------------------------------------------------------------------------------- 1 | /// out: "If you see this string, the JSON was parsed!" 2 | 3 | use "lib/json.ae" 4 | 5 | def main() { 6 | let js_text = "{\"object\": {\"title\": [0, \"If you see this string, the JSON was parsed!\"], \"Div\": {\"title\": \"hellooo\", \"item\": {\"val\": {\"ID\": \"#0\", \"Def\": {\"numba\": 100}}}}}, \"list\": [\"Hello\", [\"nested\", \"array\"], {\"nested\": {\"object\": [1, 2, \"Hi\"]}}, null, true, false]}" 7 | let json = JSON::parse_from_string(js_text) 8 | 9 | let title = json.get("object").get("title").at(1).as_str() 10 | println("%s", title.data) 11 | } -------------------------------------------------------------------------------- /tests/labelled_params.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def foo(val: i32, does_exist: bool): i32 { 4 | return val 5 | } 6 | 7 | def main() { 8 | let x = foo(1, true) 9 | let y = foo(2, does_exist: false) 10 | } -------------------------------------------------------------------------------- /tests/libs.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | use "lib/image.ae" 4 | use "lib/vec.ae" 5 | use "lib/vector.ae" 6 | use "lib/map.ae" 7 | use "lib/sdl.ae" 8 | use "lib/glut.ae" 9 | use "lib/math.ae" 10 | 11 | // Just want to make sure that the code compiles 12 | 13 | def main() {} -------------------------------------------------------------------------------- /tests/logical.ae: -------------------------------------------------------------------------------- 1 | /// out: "1 0 1 1 1 0" 2 | 3 | def main() { 4 | let a = true and true 5 | let b = true and false 6 | let c = true or false 7 | let d = not false or false 8 | let e = (false and false) or true 9 | let f = false and (false or true) 10 | println("%d %d %d %d %d %d", a, b, c, d, e, f) 11 | } -------------------------------------------------------------------------------- /tests/match.ae: -------------------------------------------------------------------------------- 1 | /// out: "be right back (not really)" 2 | 3 | enum People { 4 | LP 5 | Charlie 6 | KJ 7 | Coffee 8 | } 9 | 10 | def main() { 11 | let x = People::LP 12 | match x { 13 | People::LP => { 14 | println("be right back (not really)") 15 | } 16 | People::Charlie | 17 | People::KJ => { 18 | println("lets go invade a country") 19 | } 20 | People::Coffee => { 21 | println("idk man") 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/match_char.ae: -------------------------------------------------------------------------------- 1 | /// out: "cool" 2 | 3 | def main() { 4 | match 'f' { 5 | 'f' => { 6 | println("cool") 7 | } 8 | 'a' | 9 | 'b' | 10 | 'c' => { 11 | println("huh") 12 | } 13 | else => { 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /tests/match_expr_exit.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def main() { 4 | let x = match 1 { 5 | 0 => 3 6 | 4 => 5 7 | else => panic("oh no") 8 | } 9 | } -------------------------------------------------------------------------------- /tests/match_no_enum_name.ae: -------------------------------------------------------------------------------- 1 | /// out: "be right back (not really)" 2 | 3 | enum People { 4 | LP 5 | Charlie 6 | KJ 7 | Coffee 8 | } 9 | 10 | def main() { 11 | let x = People::LP 12 | match x { 13 | LP => { 14 | println("be right back (not really)") 15 | } 16 | Charlie | 17 | KJ => { 18 | println("lets go invade a country") 19 | } 20 | Coffee => { 21 | println("idk man") 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/match_string.ae: -------------------------------------------------------------------------------- 1 | /// out: "31" 2 | 3 | def main() { 4 | let x = "bruh" 5 | match x { 6 | "Hello" => print("1") 7 | "World" | "bruh" => print("3") 8 | "lmao" => print("4") 9 | else => print("unknown") 10 | } 11 | 12 | match x { 13 | "bruh" => print("1") 14 | "lmao" => print("4") 15 | else => print("unknown") 16 | } 17 | } -------------------------------------------------------------------------------- /tests/method_pass_this_obj_ptr.ae: -------------------------------------------------------------------------------- 1 | /// out: "16 10 16" 2 | 3 | // We can instruct methods to pass in `this` either by value or by a pointer, 4 | // denoted by `Foo::foo(this)` and `Foo::foo(&this)` respectively. 5 | // This test makes sure those semantics are correct. 6 | 7 | struct Foo { 8 | x: i32 9 | } 10 | 11 | def Foo::foo(this): Foo { 12 | .x += 1 13 | return this 14 | } 15 | 16 | def Foo::bar(&this): &Foo { 17 | .x += 1 18 | return this 19 | } 20 | 21 | def main() { 22 | let f: Foo 23 | f.x = 10 24 | let res0 = f.foo().foo().foo().foo().foo().foo() 25 | let mid = f 26 | let res1 = f.bar().bar().bar().bar().bar().bar() 27 | println("%d %d %d", res0.x, mid.x, res1.x) 28 | } -------------------------------------------------------------------------------- /tests/methods.ae: -------------------------------------------------------------------------------- 1 | /// out: "69\n1104" 2 | 3 | struct Rectangle { 4 | width: i32 5 | height: i32 6 | } 7 | 8 | def Rectangle::area(&this): i32 { 9 | return .width * .height 10 | } 11 | 12 | def Rectangle::scale(&this, factor: i32) { 13 | .width = .width * factor 14 | .height = .height * factor 15 | } 16 | 17 | def main() { 18 | let x: Rectangle 19 | x.width = 23 20 | x.height = 3 21 | println("%d", x.area()) 22 | 23 | x.scale(2) 24 | let z = &x 25 | z.scale(2) 26 | 27 | println("%d", z.area()) 28 | } 29 | -------------------------------------------------------------------------------- /tests/multiple_comparison.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def main() { 4 | let a = 1 < 2 5 | let b = 1 < 2 < 3 6 | let c = 1 < 2 < 3 < 4 7 | let d = 1 < 2 < 3 < 4 < 5 8 | } -------------------------------------------------------------------------------- /tests/nullptr.ae: -------------------------------------------------------------------------------- 1 | /// out: "pass" 2 | 3 | def foo(x: untyped_ptr): untyped_ptr { 4 | return x 5 | } 6 | 7 | def main() { 8 | let vec = null as &string 9 | if vec == null { 10 | print("pa") 11 | } 12 | // This is not ideal, but convenient for now. 13 | let x: &i32 = foo(vec) 14 | if not (null != x) { 15 | print("ss") 16 | } 17 | } -------------------------------------------------------------------------------- /tests/pointer_to_array.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | // This previously did not compile with the hacky codegen for types, 4 | // so making sure it does now. 5 | 6 | def main() { 7 | let x: [[i32; 4]; 5] 8 | x[0][0] = 1 9 | let y = x 10 | let z: &[i32; 4] = y 11 | let w = z 12 | } -------------------------------------------------------------------------------- /tests/pointers.ae: -------------------------------------------------------------------------------- 1 | /// out: "10 20\n20 10" 2 | 3 | def swap_i32(a: &i32, b: &i32) { 4 | let tmp = *a 5 | *a = *b 6 | *b = tmp 7 | } 8 | 9 | def main() { 10 | let x = 10 11 | let y = 20 12 | println("%d %d", x, y) 13 | swap_i32(&x, &y) 14 | println("%d %d", x, y) 15 | } 16 | -------------------------------------------------------------------------------- /tests/print1.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | /// out: "Hello, World\n1 2 3" 3 | 4 | def main() : i32 { 5 | println("Hello, World") 6 | println("%d %d %d", 1, 2, 3) 7 | } -------------------------------------------------------------------------------- /tests/question.ae: -------------------------------------------------------------------------------- 1 | /// out: "pass" 2 | 3 | def main() { 4 | let x = null as &i32 5 | if not x? { 6 | print("pa") 7 | } 8 | let y = 10 9 | x = &y 10 | if x? { 11 | print("ss") 12 | } 13 | } -------------------------------------------------------------------------------- /tests/relative_imports/a.ae: -------------------------------------------------------------------------------- 1 | /// skip 2 | 3 | use "@/b/b.ae" 4 | 5 | def a() { 6 | print("a ") 7 | b() 8 | } -------------------------------------------------------------------------------- /tests/relative_imports/b/b.ae: -------------------------------------------------------------------------------- 1 | /// skip 2 | 3 | use "@/b/c/c.ae" 4 | 5 | def b() { 6 | print("b ") 7 | c() 8 | } -------------------------------------------------------------------------------- /tests/relative_imports/b/c/c.ae: -------------------------------------------------------------------------------- 1 | /// skip 2 | 3 | def c() { 4 | print("c ") 5 | } -------------------------------------------------------------------------------- /tests/relative_imports/main.ae: -------------------------------------------------------------------------------- 1 | /// out: "a b c" 2 | 3 | use "@/a.ae" 4 | 5 | def main() { 6 | a() 7 | } -------------------------------------------------------------------------------- /tests/return_analysis.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | def foo(): i32 { 4 | let x = 5; 5 | return x; 6 | } 7 | 8 | def bar(): i32 { 9 | if 5 < 10 { 10 | return 10 11 | } else { 12 | return 5 13 | } 14 | } 15 | 16 | def baz(): i32 { 17 | match 5 { 18 | 1 => return 0 19 | 2 => return 0 20 | 3 => return 0 21 | else => return 0 22 | } 23 | } 24 | 25 | enum Test { 26 | One 27 | Two 28 | Three 29 | } 30 | 31 | def test0(): i32 { 32 | match Test::One { 33 | One => return 1 34 | Two => return 2 35 | else => return 3 36 | } 37 | } 38 | 39 | def test1(): i32 { 40 | match Test::One { 41 | One => return 1 42 | Two => return 2 43 | Three => return 3 44 | } 45 | } 46 | 47 | def main() { 48 | 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/return_analysis_exit.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | def bar() exits { 4 | exit(0) 5 | } 6 | 7 | def foo(): i32 { 8 | bar() 9 | } 10 | 11 | def baz(): i32 { 12 | exit(0) 13 | } 14 | 15 | def main() { 16 | foo() 17 | } -------------------------------------------------------------------------------- /tests/same_method_name.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | struct Foo {} 4 | struct Boo {} 5 | 6 | def func() {} 7 | def Foo::func() {} 8 | def Boo::func() {} 9 | 10 | def main() {} -------------------------------------------------------------------------------- /tests/strings.ae: -------------------------------------------------------------------------------- 1 | /// out: "Hello, world!\nBruh" 2 | 3 | def main() { 4 | let a: string = "Hello, world!" 5 | let b = getName() 6 | println("%s", a) 7 | println("%s", b) 8 | } 9 | 10 | def getName(): string { 11 | return "Bruh" 12 | } -------------------------------------------------------------------------------- /tests/structorder.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | struct G0 { 4 | a: G5 5 | b: G4 6 | } 7 | 8 | struct G2 { 9 | a: G5 10 | } 11 | 12 | struct G1 { 13 | a: G4 14 | b: G3 15 | } 16 | 17 | struct G3 { 18 | a: G2 19 | } 20 | 21 | struct G5 { 22 | a: i32 23 | } 24 | 25 | struct G4 { 26 | a: i32 27 | } 28 | 29 | def main() { 30 | let z: G0 31 | let a: G1 32 | let b: G2 33 | let c: G3 34 | let d: G4 35 | let e: G5 36 | } 37 | -------------------------------------------------------------------------------- /tests/structs.ae: -------------------------------------------------------------------------------- 1 | /// out: "64\n64" 2 | 3 | struct Square { 4 | x: i32 5 | y: i32 6 | } 7 | 8 | struct Shapes { 9 | s: Square 10 | b: &Square 11 | } 12 | 13 | def main() { 14 | let a: Square 15 | a.x = 5 16 | a.y = 10 17 | let z = &a 18 | z.x = 54 19 | println("%d", z.y + a.x) 20 | 21 | let sh: Shapes 22 | sh.s.x = 30 23 | sh.b = &a 24 | sh.b.y = 34 25 | println("%d", sh.s.x + sh.b.y) 26 | } 27 | -------------------------------------------------------------------------------- /tests/structs_recursive.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | 3 | struct Foo { 4 | a: i32 5 | b: &Bar 6 | } 7 | 8 | struct Bar { 9 | x: bool 10 | y: &Foo 11 | } 12 | 13 | def main() { 14 | 15 | } -------------------------------------------------------------------------------- /tests/typecheck_basic.ae: -------------------------------------------------------------------------------- 1 | /// out: "" 2 | 3 | def foo(): i32 { 4 | let x = 5 5 | let y = 7 6 | { 7 | let p = 3 8 | } 9 | return x 10 | } 11 | 12 | def main() : i32 { 13 | let x: i32 = 5 14 | let y: i32 = 7 15 | let z: bool = true 16 | { 17 | let p: i32 = 0 18 | return p 19 | } 20 | } -------------------------------------------------------------------------------- /tests/typed_int_lits.ae: -------------------------------------------------------------------------------- 1 | /// compile 2 | 3 | def main() { 4 | let x: u8 = 0x10u8 5 | let y: u16 = 0b1010u16 6 | let z: u32 = 30u32 7 | let w: u64 = 40u64 8 | let a: i8 = -10i8 9 | let b: i16 = -20i16 10 | let c: i32 = -30i32 11 | let d: i64 = -40i64 12 | let e: f32 = 50.0f32 13 | let f: f64 = 60.0f64 14 | } -------------------------------------------------------------------------------- /tests/vars.ae: -------------------------------------------------------------------------------- 1 | /// exit: 0 2 | /// out: "15" 3 | 4 | def main() : i32 { 5 | let x: i32 = 5 6 | let y: i32 = 10 7 | let z: i32 = x + y 8 | println("%d", z) 9 | } -------------------------------------------------------------------------------- /tests/while.ae: -------------------------------------------------------------------------------- 1 | /// out: "1\n1\n2\n3\n5\n8\n13\n21" 2 | 3 | def main() { 4 | let a: i32 = 0 5 | let b: i32 = 1 6 | 7 | let n: i32 = 8 8 | while n > 0 { 9 | let tmp: i32 = a 10 | a = b 11 | b = tmp + b 12 | println("%d", a) 13 | n = n - 1 14 | } 15 | } --------------------------------------------------------------------------------