diff --git a/.gitignore b/.gitignore index 2a3dc53..b9e3ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ onev *.so *.dylib *.dll +*.c +*.one # Ignore binary output folders bin/ diff --git a/generator.v b/generator.v new file mode 100644 index 0000000..ac119ab --- /dev/null +++ b/generator.v @@ -0,0 +1,132 @@ +module main + +import strings +import os + +struct Generator { +mut: + out strings.Builder +} + +fn (mut g Generator) get_c_type(typ string) string { + c_type := if typ == 'real' { 'double' } else { typ } + return c_type +} + +fn (mut g Generator) gen_stmt(stmt Stmt) { + match stmt { + VarDecl { + c_type := g.get_c_type(stmt.type) + g.out.write_string('${c_type} ${stmt.name} = ') + g.gen_expr(stmt.value) + g.out.writeln(';') + } + ExprStmt { + g.gen_expr(stmt.expr) + g.out.writeln(';') + } + ReturnStmt { + g.out.write_string('return ') + g.gen_expr(stmt.expr) + g.out.writeln(';') + } + Block { + g.out.writeln('{') + for inner_stmt in stmt.stmts { + g.gen_stmt(inner_stmt) + } + g.out.writeln('}') + } + FuncDecl { + c_type := g.get_c_type(stmt.ret_type) + g.out.write_string('${c_type} ${stmt.name}() ') + g.gen_stmt(stmt.block) + } + } +} + +fn (mut g Generator) gen_expr(expr Expr) { + match expr { + RealLiteral { + g.out.write_string(expr.val.str()) + } + IntegerLiteral { + g.out.write_string(expr.val.str()) + } + BoolLiteral { + g.out.write_string(expr.val.str()) + } + Variable { + g.out.write_string(expr.name) + } + UnaryExpr { + // Handle postfix/prefix logic if necessary + g.out.write_string('${expr.ident}${expr.op}') + } + BinaryExpr { + g.out.write_string('(') + g.gen_expr(expr.left) + g.out.write_string(' ${expr.op} ') + g.gen_expr(expr.right) + g.out.write_string(')') + } + PrintExpr { + g.out.write_string('printf(\"%') + format := match expr.type { + 'int' {'d'} + 'real' {'lf'} + 'bool' {'d'} + else {panic("Tried printing illegal type")} + } + g.out.write_string('${format}\\n\", ') + g.gen_expr(expr.expr) + g.out.write_string(')') + } + TypeCast { + c_type := g.get_c_type(expr.type) + g.out.write_string('(${c_type})') + g.gen_expr(expr.expr) + } + else {panic('unimplemented ${expr}')} + } +} + +fn (mut g Generator) gen_c(program []Stmt) string { + g.out.writeln('#include ') + g.out.writeln('#include ') // For your 'bool' types [cite: 4] + for stmt in program { + g.gen_stmt(stmt) + } + + return g.out.str() + +} + +fn compile_with_clang(c_code string, output_name string, keep_c bool) { + // 1. Write the C code to a temporary file + c_file := 'middle_c.c' + os.write_file(c_file, c_code) or { + eprintln('Failed to write C file: $err') + return + } + + // 2. Construct the clang command + // We'll use -O2 for optimization and -o to specify the output binary + clang_cmd := 'clang ${c_file} -o ${output_name} -O2' + + println('Executing: ${clang_cmd}') + + // 3. Run the command + result := os.execute(clang_cmd) + + if result.exit_code != 0 { + eprintln('Clang Compilation Failed:') + eprintln(result.output) + } else { + println('Compilation successful! Binary created: $output_name') + // Optional: Remove the temporary C file + if !keep_c { + os.rm(c_file) or { } + } + } +} diff --git a/lexer.v b/lexer.v index afe6565..a67fcd0 100644 --- a/lexer.v +++ b/lexer.v @@ -11,6 +11,7 @@ enum TokenType as u8 { kw_break kw_fn kw_return + kw_print integer real boolean @@ -99,6 +100,7 @@ fn toktype_from_kw(kw string) TokenType { 'fn' {.kw_fn} 'return' {.kw_return} 'true', 'false' {.boolean} + 'print' {.kw_print} else {.unknown} } } @@ -114,7 +116,7 @@ fn is_real(str string) bool { fn is_keyword(str string) bool { return [ - "void", "int", "real", "bool", "if", "else", "for", "break", "fn", "return", "let", "true", "false" + "void", "int", "real", "bool", "if", "else", "for", "break", "fn", "return", "let", "true", "false", "print" ].contains(str) } diff --git a/main.v b/main.v index aa8be7a..910a80a 100644 --- a/main.v +++ b/main.v @@ -1,9 +1,10 @@ module main import os +import strings fn main() { - content := os.read_file("test.one") or { return } + content := os.read_file("source.one") or { return } println("---------\n" + content + "---------") tokens := lex(content) or { return } println("-- TOK --") @@ -17,4 +18,15 @@ fn main() { println("-- AST --") println(statements) + + mut generator := Generator{ + out: strings.new_builder(100) + } + + out_c := generator.gen_c(statements) + + println("--- C ---") + println(out_c) + compile_with_clang(out_c, 'test', true) + } diff --git a/parser.v b/parser.v index 73fbc3e..65c1f9a 100644 --- a/parser.v +++ b/parser.v @@ -2,6 +2,28 @@ module main import term +// ------------------------------------------- Precedence + +enum Precedence { + lowest + assignment // = , +=, -= + comparison // ==, !=, <, > + sum // +, - + product // *, / + prefix // -x, !x + call // function() +} + +fn (p Parser) get_precedence(tok_type TokenType) Precedence { + return match tok_type { + .equals, .plus_eq, .minus_eq, .star_eq, .slash_eq { .assignment } + .eq_eq, .not_eq, .less_eq, .greater_eq { .comparison } + .plus, .minus { .sum } + .star, .slash { .product } + else { .lowest } + } +} + // ------------------------------------------- Symbol Table type SymbolInfo = VarSymbolInfo | FuncSymbolInfo @@ -70,7 +92,7 @@ fn (mut s SymbolTable) is_in_global_scope() bool { // ------------------------------------------- Expressions -type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr +type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr struct VoidExpr {} @@ -118,6 +140,11 @@ struct ParenExpr { expr Expr } +struct PrintExpr { + expr Expr + type string +} + // ------------------------------------------- Statements type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | FuncDecl @@ -139,7 +166,7 @@ struct ExprStmt { } struct ReturnStmt { - value Expr + expr Expr } struct Block { @@ -210,20 +237,20 @@ fn (mut p Parser) parse_primary() Expr { .identifier {p.parse_ident(token.text)} .type {p.parse_type(token.text)} .lparen {p.parse_paren()} + .kw_print {p.parse_print()} else {parse_error("Unexpected Token")} } } -fn (mut p Parser) parse_expr() Expr { - mut left := p.parse_primary() +fn (mut p Parser) parse_expr(prec Precedence) Expr { + mut expr := p.parse_primary() - match p.peek().type { - .plus, .minus, .star, .slash, .equals, .eq_eq, .not_eq, .less_eq, .greater_eq, - .plus_eq, .minus_eq, .star_eq, .slash_eq { - return p.parse_binary(left, p.peek().text) - } - else {return left} + for int(prec) < int(p.get_precedence(p.peek().type)) { + op_tok := p.next() + expr = p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type)) } + + return expr } fn (mut p Parser) parse_ident(ident string) Expr { @@ -233,10 +260,19 @@ fn (mut p Parser) parse_ident(ident string) Expr { } } -fn (mut p Parser) parse_binary(left Expr, op string) BinaryExpr { - p.next() - right := p.parse_expr() +fn (mut p Parser) parse_print() PrintExpr { + p.expect(.lparen) + expr := p.parse_expr(.lowest) + p.expect(.rparen) + return PrintExpr{expr: expr, type: p.get_expr_type(expr)} +} + +fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr { + //p.next() + right := p.parse_expr(prec) binary_expr := BinaryExpr{left, op, right} + + if !p.is_op_valid_for_type(p.get_expr_type(left), op) { parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}") } @@ -248,7 +284,7 @@ fn (mut p Parser) parse_type(type string) Expr { if p.peek().type == .lparen { p.next() - expr := p.parse_expr() + expr := p.parse_expr(.lowest) p.expect(.rparen) return TypeCast { @@ -261,7 +297,7 @@ fn (mut p Parser) parse_type(type string) Expr { } fn (mut p Parser) parse_paren() ParenExpr { - expr := p.parse_expr() + expr := p.parse_expr(.lowest) p.expect(.rparen) return ParenExpr{expr: expr} } @@ -356,7 +392,7 @@ fn (mut p Parser) parse_var_decl() VarDecl { } p.expect(.equals) - val := p.parse_expr() + val := p.parse_expr(.lowest) if type_tok.text == 'void' { parse_error("Cannot declare a variable of type void") @@ -402,9 +438,9 @@ fn (mut p Parser) parse_func_decl() FuncDecl { return_stmts := p.get_return_stmts_recursive(block) for return_stmt in return_stmts { - if p.get_expr_type(return_stmt.value) != type_tok.text { + if p.get_expr_type(return_stmt.expr) != type_tok.text { parse_error("Mismatch between declared return type (${type_tok.text}) \ - and actual return type (${p.get_expr_type(return_stmt.value)})") + and actual return type (${p.get_expr_type(return_stmt.expr)})") } } @@ -437,12 +473,12 @@ fn (mut p Parser) parse_return_stmt() ReturnStmt { p.expect(.semicolon) } return ReturnStmt { - value: expr + expr: expr } } fn (mut p Parser) parse_expr_stmt() ExprStmt { - expr := p.parse_expr() + expr := p.parse_expr(.lowest) p.expect(.semicolon) return ExprStmt { diff --git a/test b/test new file mode 100755 index 0000000..f080ec6 Binary files /dev/null and b/test differ diff --git a/test.one b/test.one deleted file mode 100644 index f01d57c..0000000 --- a/test.one +++ /dev/null @@ -1,3 +0,0 @@ -let a int = 2; -a++; -a *= 3.6 + 1.1;