From 84413ca1bcddd5f0a887ea8108e0682817f2d410 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Wed, 11 Jun 2025 00:23:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3parser=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/modbus/src/modbus_parser.erl | 89 ++++++++++++++++++++----------- apps/modbus/src/test.erl | 2 +- modbus.conf | 8 +-- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/apps/modbus/src/modbus_parser.erl b/apps/modbus/src/modbus_parser.erl index eac1921..3a8642a 100644 --- a/apps/modbus/src/modbus_parser.erl +++ b/apps/modbus/src/modbus_parser.erl @@ -43,21 +43,21 @@ lexer(<<$#, Rest/binary>>, Line, _Current, Acc) -> lexer(NewRest, Line + 1, [], [{comment, Line, Comment}|Acc]); %% 处理特殊字符开头的行, ‘$=’ 不会是一行的开头 lexer(<<${, Rest/binary>>, Line, [], Acc) -> - lexer(Rest, Line, [], [{'{', Line}|Acc]); + lexer(Rest, Line, [], [{special, Line, ${}|Acc]); lexer(<<$}, Rest/binary>>, Line, [], Acc) -> - lexer(Rest, Line, [], [{'}', Line}|Acc]); + lexer(Rest, Line, [], [{special, Line, $}}|Acc]); lexer(<<$;, Rest/binary>>, Line, [], Acc) -> - lexer(Rest, Line, [], [{';', Line}|Acc]); + lexer(Rest, Line, [], [{special, Line, $;}|Acc]); %% 在行中遇到特殊字符的处理逻辑 lexer(<>, Line, Current, Acc) -> case is_special(Char) of true -> case Current of [] -> - lexer(Rest, Line, [], [{special, Line, list_to_binary([Char])}|Acc]); + lexer(Rest, Line, [], [{special, Line, Char}|Acc]); _ -> Ident = list_to_binary(string:trim(lists:reverse(Current))), - lexer(Rest, Line, [], [{special, Line, list_to_binary([Char])}, {ident, Line, Ident}|Acc]) + lexer(Rest, Line, [], [{special, Line, Char}, {ident, Line, Ident}|Acc]) end; false -> lexer(Rest, Line, [Char|Current], Acc) @@ -79,32 +79,61 @@ is_special(_) -> false. %% 语法分析:将标记序列转换为AST parser(Tokens) -> - lists:foreach(fun(T) -> - lager:debug("token is: ~p", [T]) - end, Tokens), - parser(Tokens, []). + %display_tokens(Tokens), + parser(Tokens, 0, []). -parser([], Result) -> {ok, Result}; -parser([{'{', _}|Tokens], Stack) -> - parser(Tokens, [open_block|Stack]); -parser([{'}', _}|Tokens], [open_block, {block, Type, Props}|Stack]) -> - Block = #{type => Type, properties => lists:reverse(Props)}, - parser(Tokens, [Block|Stack]); -parser([{ident, _, <<"modbus">>}|Tokens], Stack) -> - parser(Tokens, [{block, modbus, []}|Stack]); -parser([{ident, _, <<"device">>}, {ident, _, Name}|Tokens], Stack) -> - parser(Tokens, [{block, {device, Name}, []}|Stack]); -parser([{ident, _, <<"processor">>}, {ident, _, Name}|Tokens], Stack) -> - parser(Tokens, [{block, {processor, Name}, []}|Stack]); -parser([{ident, _, <<"alarm">>}, {ident, _, Name}|Tokens], Stack) -> - parser(Tokens, [{block, {alarm, Name}, []}|Stack]); -parser([{ident, _Line, Name}, {special, _, $=}|Tokens], [{block, _, Props}|Stack]) -> - {Value, Rest} = parse_value(Tokens, []), - parser(Rest, [{block, Props#{binary_to_atom(Name, utf8) => Value}}|Stack]); -parser([{';', _}|Tokens], Stack) -> - parser(Tokens, Stack); -parser([{comment, _, _}|Tokens], Stack) -> - parser(Tokens, Stack). +display_tokens(Tokens) -> + lists:foreach(fun(T) -> lager:debug("token is: ~p", [T]) end, Tokens). + +%% 将tokens解析成block + +parser([], 0, Blocks0) -> + Blocks = lists:reverse(Blocks0), + lager:debug("parse result: ~p", [Blocks]), + {ok, Blocks}; + +%% 这里很重要,要到Block的关闭字符 +parser([{special, _, $}}|Tokens], 1, Stack) -> + parser(Tokens, 0, Stack); +parser([{special, _, $}}|Tokens], Level, Stack) -> + parser(Tokens, Level - 1, Stack); + +parser([{special, _, $;}|Tokens], Level, Stack) -> + parser(Tokens, Level, Stack); +%% 处理空的定义 +parser([{ident, _, <<>>}|Tokens], Level, Stack) -> + parser(Tokens, Level, Stack); + +%% 预定义支持的标签, 顶层定义, 遇到是,目前的level值必须等于:0 +parser([{ident, _, <<"modbus">>}, {special, _, ${} |Tokens], 0, Stack) -> + parser(Tokens, 1, [{block, modbus, []}|Stack]); +parser([{ident, _, <<"device", Name/binary>>}, {special, _, ${}|Tokens], 0, Stack) -> + parser(Tokens, 1, [{block, {device, string:trim(Name)}, []}|Stack]); +parser([{ident, _, <<"processor", Name/binary>>}, {special, _, ${}|Tokens], 0, Stack) -> + parser(Tokens, 1, [{block, {processor, string:trim(Name)}, []}|Stack]); +parser([{ident, _, <<"alarm", Name/binary>>}, {special, _, ${}|Tokens], 0, Stack) -> + parser(Tokens, 1, [{block, {alarm, string:trim(Name)}, []}|Stack]); + +%% 运行被嵌套的定义 +parser([{ident, _, <<"transport", Proto/binary>>}, {special, _, ${} | Tokens], Level, Stack) -> + parser(Tokens, Level + 1, [{block, {transport, string:trim(Proto)}, []}|Stack]); +parser([{ident, _, <<"variables">>}, {special, _, ${}|Tokens], Level, Stack) -> + parser(Tokens, Level + 1, [{block, variables, []}|Stack]); +%% 非标准定义 +parser([{ident, _, BlockName}, {special, _, ${}|Tokens], Level, Stack) -> + parser(Tokens, Level + 1, [{block, BlockName, []}|Stack]); + +%% 其他定义,是基于: port /dev/ttyUSB0; 这样的格式的 +parser([{ident, _Line, Prop}, {special, _, $;}|Tokens], Level, [{block, Block, Props}|Stack]) -> + parser(Tokens, Level, [{block, Block, [Prop|Props]}|Stack]); + +%% todo +%parser([{ident, _Line, Name}, {special, _, $=}|Tokens], [{block, _, Props}|Stack]) -> +% {Value, Rest} = parse_value(Tokens, []), +% parser(Rest, [{block, Props#{binary_to_atom(Name, utf8) => Value}}|Stack]); + +parser([{comment, _, _}|Tokens], Level, Stack) -> + parser(Tokens, Level, Stack). parse_value([{ident, _, Value}|Tokens], _) -> {binary_to_atom(Value, utf8), Tokens}; parse_value([{integer, _, Value}|Tokens], _) -> {Value, Tokens}; diff --git a/apps/modbus/src/test.erl b/apps/modbus/src/test.erl index d7926b4..36dc462 100644 --- a/apps/modbus/src/test.erl +++ b/apps/modbus/src/test.erl @@ -14,7 +14,7 @@ test() -> {ok, Config} = file:read_file("/usr/local/code/cloudkit/modbus/modbus.conf"), - lager:debug("config is: ~ts", [Config]), + %lager:debug("config is: ~ts", [Config]), {ok, AST} = modbus_parser:parse(Config), modbus_parser:validate(AST), diff --git a/modbus.conf b/modbus.conf index eb259be..1e4c78c 100644 --- a/modbus.conf +++ b/modbus.conf @@ -107,10 +107,10 @@ processor temperature_processor { } # 输出目标 - output { - mqtt "sensors/boiler/temp"; - influxdb "plant_metrics" measurement="temperature"; - } + #output { + # mqtt "sensors/boiler/temp"; + # influxdb "plant_metrics" measurement="temperature"; + #} } alarm high_temperature {