LuaLS / LuaLS/lua-language-server

[Library] A native Luals parser

Open
#2,910 9 comments 10 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

rust
Dominant language
Lua
Stars
4.4k
Forks
442
PR merge metrics
No merged PRs in 30d

Description

I have completed most of the work, but there may still be many bugs in the details that I will gradually fix in future use. I will implement a new language server based on this. If you are interested, you can continue to follow my progress.

the library: https://crates.io/crates/emmylua_parser

a simple example:

let code = r#"
    local a = 1
    local b = 2
    local function foo(x)
        return x + 1
    end
    function f()
    end
    local c = foo(a + b)
    if c > 2 then
        print(c)
    elseif c == 2 then
    else
        print("c is not greater than 2")
    end
    for i = 1, 10 do
        print(i)
    end
    for i, v in ipairs({1, 2, 3}) do
        print(i, v)
    end
    while c < 10 do
        c = c + 1
    end
    repeat
        c = c - 1
    until c == 0
    ::ll::
    goto ll
    local a = c["string"]
"#;
let tree = LuaParser::parse(code, ParserConfig::default());
let chunk = tree.get_chunk_node();
for node in chunk.descendants::<LuaAst>() {
    match node {
        LuaAst::LuaChunk(lua_chunk) => {
            assert!(lua_chunk.get_block().is_some());
        }
        LuaAst::LuaBlock(lua_block) => {
            assert!(lua_block.get_stats().next().is_some());
        }
        LuaAst::LuaAssignStat(lua_assign_stat) => {
            let (var_list, expr_list) = lua_assign_stat.get_var_and_expr_list();
            assert!(var_list.len() > 0);
            assert!(expr_list.len() > 0);
        }
        LuaAst::LuaLocalStat(lua_local_stat) => {
            let mut name_list = lua_local_stat.get_local_name_list();
            let local_name = name_list.next().unwrap();
            assert!(local_name.get_name_token().is_some());
            let mut expr_list = lua_local_stat.get_value_exprs();
            assert!(expr_list.next().is_some());
        }
        LuaAst::LuaCallExprStat(lua_call_expr_stat) => {
            assert!(lua_call_expr_stat.get_call_expr().is_some());
        }
        LuaAst::LuaLabelStat(lua_label_stat) => {
            assert!(lua_label_stat.get_label_name_token().is_some());
            assert_eq!(
                lua_label_stat
                    .get_label_name_token()
                    .unwrap()
                    .get_name_text(),
                "ll"
            );
        }
        LuaAst::LuaGotoStat(lua_goto_stat) => {
            assert!(lua_goto_stat.get_label_name_token().is_some());
            assert_eq!(
                lua_goto_stat
                    .get_label_name_token()
                    .unwrap()
                    .get_name_text(),
                "ll"
            );
        }
        LuaAst::LuaDoStat(lua_do_stat) => {
            assert!(lua_do_stat.get_block().is_some());
        }
        LuaAst::LuaWhileStat(lua_while_stat) => {
            assert!(lua_while_stat.get_condition_expr().is_some());
            assert!(lua_while_stat.get_block().is_some());
        }
        LuaAst::LuaRepeatStat(lua_repeat_stat) => {
            assert!(lua_repeat_stat.get_block().is_some());
            assert!(lua_repeat_stat.get_condition_expr().is_some());
        }
        LuaAst::LuaIfStat(lua_if_stat) => {
            assert!(lua_if_stat.get_condition_expr().is_some());
            assert!(lua_if_stat.get_block().is_some());
            assert!(lua_if_stat.get_else_if_clause_list().next().is_some());
            assert!(lua_if_stat.get_else_clause().is_some());
        }
        LuaAst::LuaForStat(lua_for_stat) => {
            assert!(lua_for_stat.get_var_name().is_some());
            assert!(lua_for_stat.get_block().is_some());
            assert_eq!(lua_for_stat.get_iter_expr().count(), 2);
        }
        LuaAst::LuaForRangeStat(lua_for_range_stat) => {
            assert_eq!(lua_for_range_stat.get_var_name_list().count(), 2);
            assert!(lua_for_range_stat.get_block().is_some());
            assert!(lua_for_range_stat.get_expr_list().next().is_some());
        }
        LuaAst::LuaFuncStat(lua_func_stat) => {
            assert!(lua_func_stat.get_func_name().is_some());
            assert!(lua_func_stat.get_closure().is_some());
        }
        LuaAst::LuaLocalFuncStat(lua_local_func_stat) => {
            assert!(lua_local_func_stat.get_local_name().is_some());
            assert!(lua_local_func_stat.get_closure().is_some());
        }
        LuaAst::LuaReturnStat(lua_return_stat) => {
            assert!(lua_return_stat.get_expr_list().next().is_some());
        }
        LuaAst::LuaNameExpr(lua_name_expr) => {
            assert!(lua_name_expr.get_name_token().is_some());
        }
        LuaAst::LuaIndexExpr(lua_index_expr) => {
            assert!(lua_index_expr.get_prefix_expr().is_some());
            assert!(lua_index_expr.get_indexed_expr().is_some());
        }
        LuaAst::LuaTableExpr(lua_table_expr) => {
            assert!(lua_table_expr.is_array());
        }
        _ => {}
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked emmylua_parser crate and the example using LuaParser::parse and get_chunk_node(), then compare its AST coverage with the language server repository. The issue does not name repository files, tests, integration points, or acceptance criteria, so the definition of done remains unclear.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua, rust
Domain
compilers, devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
18/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.