Token :: struct { kind: Kind; str: string; Kind :: enum { invalid; end_of_file; symbol; number; string; kw_var; kw_def; kw_type; kw_do; kw_end; equal :: #char "="; plus :: #char "+"; minus :: #char "-"; star :: #char "*"; f_slash :: #char "/"; b_slash :: #char "\\"; l_paren :: #char "("; r_paren :: #char ")"; l_square :: #char "["; r_square :: #char "]"; l_brace :: #char "{"; r_brace :: #char "}"; comma :: #char ","; dot :: #char "."; colon :: #char ":"; semicolon :: #char ";"; } } Node :: struct { kind: Kind; Kind :: enum { invalid; decl_start; var; decl_end; expr_start; symbol; literal; expr_end; } } Node_Var :: struct { #as using n: Node; n.kind = .var; symbol: *Node; type_expr: *Node; value_expr: *Node; } Node_Symbol :: struct { #as using n: Node; n.kind = .symbol; str: string; } Node_Literal :: struct { #as using n: Node; n.kind = .literal; union { } } Parser :: struct { allocator: Allocator; toplevel: [..]*Node; previous: Token; filename: string; source: string; offset: int; } init :: (p: *Parser, allocator: Allocator) { p.allocator = allocator; p.toplevel.allocator = allocator; } parse_string :: (p: *Parser, source: string) -> bool { p.source = source; p.offset = 0; while !at_end(p) { t := peek_token(p); if t.kind == .invalid || t.kind == .end_of_file { break; } node := parse_toplevel_declaration(p); if node == null break; array.append(*p.toplevel, node); } return false; } #scope_file; parse_toplevel_declaration :: (p: *Parser) -> *Node { t, ok := expect_token(p, .kw_var); if ok == false return null; s:, ok = expect_token(p, .symbol); if ok == false return null; type_expr: *Node; value_expr: *Node; // var sym int // var sym int = value // var sym = value t = peek_token(p); if t.kind == .equal { consume_token(p); value_expr = parse_expression(p); } else { type_expr = parse_type_expression(p); if type_expr == null return null; if peek_token(p).kind == .equal { consume_token(p); value_expr = parse_expression(p); } } symbol := make_node(p, Node_Symbol); symbol.str = s.str; node := make_node(p, Node_Var); node.symbol = symbol; node.type_expr = type_expr; node.value_expr = value_expr; return node; } parse_type_expression :: (p: *Parser) -> *Node { t, ok := expect_token(p, .symbol, .star); if ok == false return null; if t.kind == { case .symbol; node := make_node(p, Node_Symbol); node.str = t.str; return node; } return null; } parse_expression :: (p: *Parser) -> *Node { return null; } make_node :: (p: *Parser, $T: Type) -> *T { return mem.request_memory(T,, allocator = p.allocator); } peek_token :: (p: *Parser) -> Token { copy := p.*; return consume_token(*copy); } at_end :: (p: *Parser) -> bool { return p.offset >= p.source.count; } starts_symbol :: (c: u8) -> bool { return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c == "_"); } continues_symbol :: (c: u8) -> bool { return starts_symbol(c) || (c >= "0" && c <= "9"); } starts_number :: (c: u8) -> bool { return (c >= "0" && c <= "9"); } continues_number :: (c: u8) -> bool { return starts_number(c) || c == "."; } consume_token :: (p: *Parser) -> Token { if at_end(p) return .{ kind = .end_of_file }; c := p.source[p.offset]; while !at_end(p) { c = p.source[p.offset]; if c == { case " "; #through; case "\n"; #through; case "\t"; p.offset += 1; case; break; } } if starts_symbol(c) { t := Token.{ str = .{ data = p.source.data + p.offset } }; while !at_end(p) { c = p.source[p.offset]; if !continues_symbol(c) break; p.offset += 1; } t.str.count = (p.source.data + p.offset) - t.str.data; if t.str == { case "var"; t.kind = .kw_var; case "def"; t.kind = .kw_def; case "do"; t.kind = .kw_do; case "end"; t.kind = .kw_end; case "type"; t.kind = .kw_type; case; t.kind = .symbol; } return t; } if starts_number(c) { t := Token.{ kind = .number, str = .{ data = p.source.data + p.offset } }; while !at_end(p) { c = p.source[p.offset]; if !continues_number(c) break; p.offset += 1; } t.str.count = (p.source.data + p.offset) - t.str.data; return t; } if c == { case "+"; #through; case "-"; #through; case "*"; #through; case "/"; #through; case "="; #through; case "("; #through; case ")"; #through; case "["; #through; case "]"; #through; case "{"; #through; case "}"; s := string.{ data = p.source.data + p.offset, count = 1 }; p.offset += 1; return .{ kind = xx c, str = s }; } s := string.{ data = p.source.data + p.offset, count = 1 }; return .{ kind = .invalid, str = s }; } expect_token :: (p: *Parser, kinds: ..Token.Kind) -> Token, bool { t := consume_token(p); for kinds if it == t.kind { return t, true; } return t, false; } #run { parser: Parser; init(*parser, context.allocator); ok := parse_string(*parser, #string END var x = 10 var y = 20 END); } mem :: #import "jc/memory"; array :: #import "jc/array"; basic :: #import "Basic"; // @future strings :: #import "String"; // @future