From 4dd4663aae98c960e858a6b01f835ee0aef74800 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Tue, 5 Mar 2024 17:05:51 -0500 Subject: [PATCH 1/6] Using ParserCombinator instead --- Project.toml | 3 +- src/BaseModelica.jl | 3 +- src/parser.jl | 423 ++++++++++++++++++++++++++------------------ src/scratch.jl | 36 ++++ 4 files changed, 289 insertions(+), 176 deletions(-) create mode 100644 src/scratch.jl diff --git a/Project.toml b/Project.toml index 30651b2..ec03f83 100644 --- a/Project.toml +++ b/Project.toml @@ -4,12 +4,11 @@ authors = ["jClugstor and contributors"] version = "1.0.0" [deps] -Automa = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +ParserCombinator = "fae87a5f-d1ad-5cf0-8f61-c941e1580b46" [compat] Aqua = "0.8" -Automa = "1" ModelingToolkit = "8.75, 9" SafeTestsets = "0.1" Test = "1.10" diff --git a/src/BaseModelica.jl b/src/BaseModelica.jl index dcc5db7..c5d4b77 100644 --- a/src/BaseModelica.jl +++ b/src/BaseModelica.jl @@ -1,7 +1,7 @@ module BaseModelica using ModelingToolkit -using Automa +using ParserCombinator """ Holds the name of the package, the models in the package, and eventually BaseModelica records. @@ -33,6 +33,7 @@ end struct BaseModelicaVariable type::Any name::Any + input_or_output::Any description::Any end diff --git a/src/parser.jl b/src/parser.jl index 6aae722..037e437 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -1,179 +1,256 @@ -base_Modelica_machine = let - newline = re"\n" | re"\r" | re"\r\n" - endexpr = re";" - description = re"(\"([A-Za-z0-9._ ']|(\n|\r|\r\n))*\")" - model_header = re"model '[A-Za-z0-9._]+'" * opt(' ' * description) - package_header = re"package '[A-Za-z0-9._]+'" - end_marker = re"end '[A-Za-z0-9._]+'" * endexpr - type = re"Real" - name = re"'[A-Za-z0-9._]+'" - value = re"= ?[0-9]+\.?[0-9]*" - - parameter = re"parameter" * ' ' * type * ' ' * name * opt(' ') * opt(value) * - opt(opt(' ') * opt(description)) * endexpr - variable = type * ' ' * name * ' ' * description * endexpr - #parameter Real '[A-Za-z0-9._]+' ?(=? ?[\d]+\.?[\d]*)? ?("([A-Za-z0-9._ ]|\n)*")?; - equation_expr = re"[^Ripem;(\n|\r|\r\n)\t ][^!=;\"\t]+ ?= ?[^!=;\"\t]+" * - opt(' ' * description) * - endexpr - equation_header = re"equation" - initial_header = re"initial equation" - - onfinal!(model_header, :get_model_name) - - onfinal!(package_header, :get_package_name) - - onfinal!(equation_header, [:set_equation_flag, :clear_initial_flag]) - onfinal!(initial_header, [:set_initial_flag, :clear_equation_flag]) - - precond!(type, :equation_or_initial_flag, bool = false) - onenter!(type, :mark_pos) - onfinal!(type, :get_type) - - precond!(name, :equation_or_initial_flag, bool = false) - onenter!(name, :mark_pos) - onexit!(name, :get_name) - - precond!(value, :equation_or_initial_flag, bool = false) - onenter!(value, :mark_pos) - onexit!(value, :get_value) - - #precond!(description, :equation_or_initial_flag, bool = false) - onenter!(description, :mark_pos) - onfinal!(description, :get_description) - - precond!(parameter, :equation_or_initial_flag, bool = false) - onfinal!(parameter, :create_parameter) - - precond!(variable, :equation_or_initial_flag, bool = false) - onfinal!(variable, :create_variable) - - precond!(equation_expr, :equation_or_initial_flag) - onenter!(equation_expr, :mark_equ_pos) - onfinal!(equation_expr, :create_equation) - - full_machine = package_header * newline * rep('\t') * rep(' ') * - model_header * newline * - rep(rep(rep('\t') * rep(' ') * parameter * - opt(newline)) * - rep(rep('\t') * rep(' ') * variable * opt(newline))) * - opt(rep('\t') * rep(' ') * initial_header * newline) * - rep(rep('\t') * - rep(' ') * equation_expr * opt(newline)) * - opt(rep('\t') * rep(' ') * - equation_header * newline) * - rep(rep('\t') * rep(' ') * equation_expr * - opt(newline)) * rep('\t') * rep(' ') * end_marker * newline * - rep('\t') * - rep(' ') * end_marker * opt(newline) - - compile(full_machine) -end +using ParserCombinator -base_Modelica_actions = Dict( - :mark_pos => :(pos = p), - :mark_equ_pos => :(equ_pos = p), - :get_type => :(type = String(data[pos:p]); pos = 0), - :get_name => :(name = Symbol(strip(String(data[pos:p]), ['\'', ' ', ';'])); pos = 0), - :get_model_name => quote - data_to_point = data[findfirst("model", data)[end]:p] - ticks = findall("'", data_to_point) - first_tick_index, last_tick_index = (only(ticks[1]), only(ticks[2])) - model_name = strip(String(data_to_point[first_tick_index:last_tick_index]), '\'') - model_description = description - description = "" - end, - :get_package_name => quote - data_to_point = data[findfirst("package", data)[end]:p] - ticks = findall("'", data_to_point) - first_tick_index, last_tick_index = (only(ticks[1]), only(ticks[2])) - package_name = strip(String(data_to_point[first_tick_index:last_tick_index]), '\'') - end, - :get_description => :(description = strip(String(data[pos:p]), '"'); pos = 0), - :get_value => :(value = strip(String(data[pos:p]), ['=', ' ', ';']); pos = 0), #get the value - :create_equation => quote - equal_index = findfirst("=", data[equ_pos:p])[1] - description_start = findfirst("\"", data[equ_pos:p]) - lhs = strip(String(data[equ_pos:p][1:(equal_index - 1)]), ' ') #data[pos:p] is the whole equation expression - rhs = strip( - isnothing(description_start) ? String(data[equ_pos:p][(equal_index + 1):end]) : - String(data[equ_pos:p][(equal_index + 1):description_start[1]]), - [' ', ';', '"']) - equation_description = description - initial_flag ? - push!(initial_equations, - BaseModelicaInitialEquation(lhs, rhs, equation_description)) : - push!(equations, BaseModelicaEquation(lhs, rhs, equation_description)) - pos = 0 - equ_pos = 0 - rhs = "" - lhs = "" - description = "" - end, - :create_parameter => :(push!( - parameters, BaseModelicaParameter(type, name, value, description)); - type = ""; - name = ""; - value = ""; - description = ""), - :create_variable => :(push!(variables, BaseModelicaVariable(type, name, description)); type = ""; name = ""; description = ""), - :set_equation_flag => :(equation_flag = true), - :clear_equation_flag => :(equation_flag = false), - :set_initial_flag => :(initial_flag = true), - :clear_initial_flag => :(initial_flag = false), - :equation_or_initial_flag => :(equation_flag || initial_flag) -) - -context = CodeGenContext(generator = :goto) -@eval function parse_package_str(data) - # Initialize variables you use in the action code. - pos = 0 - equ_pos = 0 - other_pos = 0 - equation_flag = false - initial_flag = false - - model_name = "" - model_description = "" - package_name = "" - name = "" - description = "" - value = "" - type = "" - lhs = "" - rhs = "" - parameters = BaseModelicaParameter[] - variables = BaseModelicaVariable[] - equations = BaseModelicaEquation[] - initial_equations = BaseModelicaInitialEquation[] - - # Generate code for initialization and main loop - $(generate_code(context, base_Modelica_machine, base_Modelica_actions)) - # Finally, return records accumulated in the action code. - return BaseModelicaPackage(package_name, - BaseModelicaModel(model_name, model_description, parameters, - variables, equations, initial_equations)) - #parameters, variables, initial_equations, model_name, equations -end +list2string(x) = reduce(*,x) +spc = Drop(Star(Space())) + +# Base Modelica grammar +@with_names begin +NL = p"\r\n" | p"\n" | p"\r"; +WS = p" " | p"\t" | NL; +LINE_COMMENT = p"//[^\r\n]*" + NL; +ML_COMMENT = p"/[*]([^*]|([*][^/]))*[*]/"; + +#lexical units, not keywords +NONDIGIT = p"_|[a-z]|[A-Z]"; +DIGIT = p"[0-9]"; +UNSIGNED_INTEGER = Plus(DIGIT); +Q_CHAR = NONDIGIT | DIGIT | p"[-!#$%&()*>+,./:;<>=?>@[]{}|~ ^]"; +S_ESCAPE = p"\\['\"?\\abfnrtv]"; +S_CHAR = NL | p"[^\r\n\\\"]"; +Q_IDENT = E"'" + (Q_CHAR | S_ESCAPE ) + Star(Q_CHAR | S_ESCAPE | E"\"" ) + E"'"; +IDENT = ((NONDIGIT + Star( DIGIT | NONDIGIT )) | Q_IDENT) |> list2string; +STRING = e"\"" + Star( S_CHAR | S_ESCAPE ) + e"\"" |> list2string; +EXPONENT = ( e"e" | e"E" ) + ( e"+" | e"-" )[0:1] + DIGIT[1:end]; +UNSIGNED_NUMBER = DIGIT[1:end] + ( e"." + Star(DIGIT) )[0:1] + EXPONENT[0:1] |> (x -> parse(Float64, reduce(*,x))); + +#component clauses +name = (IDENT + Star(e"." + IDENT)) |> list2string; +type_specifier = E"."[0:1] + name; +type_prefix = (( e"discrete" | e"parameter" | e"constant" )[0:1] + spc + ( e"input" | e"output" )[0:1]); +array_subscripts = Delayed() +modification = Delayed() +declaration = IDENT + array_subscripts[0:1] + modification[0:1]; +comment = Delayed() +component_declaration = declaration + spc + comment; +global_constant = e"constant" + type_specifier + array_subscripts[0:1] + declaration + comment; +component_list = (component_declaration & spc & Star(E"," + component_declaration)); +component_reference = E"."[0:1] + IDENT + array_subscripts[0:1] + Star(E"." + IDENT + array_subscripts[0:1]); +component_clause = type_prefix[1:1,:&] + spc + type_specifier + spc + component_list[1:1,:&] > create_component; +#equations + +#modification +string_comment = (STRING + Star(E"+" + STRING))[0:1]; +element_modification = name + modification[0:1] + string_comment; +element_modification_or_replaceable = element_modification; +decoration = E"@" + UNSIGNED_INTEGER; +argument = decoration[0:1] + element_modification_or_replaceable; +argument_list = argument + Star(E"," + argument); +class_modification = E"(" + argument_list[0:1] + E")"; +expression = Delayed() +modification.matcher = (class_modification + (spc + E"=" + spc + expression)[0:1]) | (spc + E"=" + spc + expression) | (E":=" + spc + expression); + +#expressions +relational_operator = e"<" | e"<=" | e">" | e">=" | e"==" | e"<>"; +add_operator = e"+" | e"-" | e".+" | e".-"; +mul_operator = e"*" | e"/" | e".*" | e"./"; + +for_index = IDENT + E"in" + expression; + +named_arguments = Delayed() +function_partial_application = E"function" + type_specifier + E"(" + named_arguments + E")"; +function_argument = function_partial_application | expression; +function_arguments_non_first = Delayed() +function_arguments_non_first.matcher = (function_argument + (E"," + function_arguments_non_first)[0:1]) | named_arguments; +named_argument = IDENT + E"=" + function_argument; +named_arguments.matcher = named_argument + Star(E"," + named_argument); +function_partial_applications = E"function" + type_specifier + E"(" + named_arguments[0:1] + E")"; +function_arguments = (expression + (E"," + function_arguments_non_first) | (E"for" + for_index)[0:1]) | + (function_partial_application + (E"," + function_arguments_non_first)[0:1]) | + named_arguments; +function_call_args = E"(" + function_arguments[0:1] + E")"; +output_expression_list = Delayed() +expression_list = Delayed() +array_arguments = expression + (Star(E"," + expression) | E"for" + for_index); +primary = UNSIGNED_NUMBER | STRING | e"false" | e"true" | + ((e"der" | e"initial" | e"pure") + function_call_args) | + (component_reference + function_call_args[0:1]) | + (E"(" + output_expression_list + E")" + array_subscripts[0:1]) | + (E"[" + expression_list + Star(E";" + expression_list) + E"]") | + (E"{" + array_arguments + E"}") | + E"end"; +factor = primary + ((E"^" | E".^") + primary)[0:1]; +term = factor + spc + Star(mul_operator + spc + factor); +arithmetic_expression = add_operator[0:1] + spc + term + spc + Star(add_operator + spc + term); + +subscript = E":" | expression; +array_subscripts.matcher = E"[" + subscript + Star(E"," + subscript); +annotation_comment = E"annotation" + class_modification; +comment.matcher = string_comment + annotation_comment[0:1]; + +enumeration_literal = IDENT + comment; +enum_list = enumeration_literal + Star(E"," + enumeration_literal); + +guess_value = E"guess" + E"(" + component_reference + E")" ; +prioritize_expression = Delayed() +parameter_equation = E"parameter equation" + guess_value + E"=" + (expression | prioritize_expression) + comment; + +normal_element = component_clause; + + +generic_element = normal_element | parameter_equation; + +language_specification = STRING; + +external_function_call = (component_reference + E"=")[0:1] + IDENT + E"(" + expression_list[0:1] + E")"; + +equation = Delayed() +initial_equation = Delayed() +statement = Delayed() +base_partition = Delayed() +composition = Star(decoration[0:1] + generic_element + E";") + + Star((e"equation" + Star(equation + E";")) | + (e"initial equation" + Star(initial_equation + E";")) | + (e"initial"[0:1] + e"algorithm" + Star(statement + E";"))) + (decoration[0:1] + E"external" + language_specification[0:1] + external_function_call[0:1] + annotation_comment[0:1] + E";")[0:1] + + Star(base_partition) + (annotation_comment + E";")[0:1]; + + +base_prefix = e"input" | e"output" +long_class_specifier = IDENT + string_comment + composition + E"end"; +short_class_specifier = IDENT + E"=" + (base_prefix[0:1] + type_specifier + class_modification[0:1]) | + (e"enumeration" + E"(" + (enum_list[0:1] | E":" ) + E")") + comment; +class_prefixes = e"type" | e"record" | ((e"pure constant")[0:1] | e"impure")[0:1] + e"function"; +der_class_specifier = IDENT + E"=" + E" "[0:1] + E"der" + E" " + E"(" + type_specifier + E"," + IDENT + Star(E"," + IDENT) + E")" + comment; +class_specifier = long_class_specifier | short_class_specifier | der_class_specifier; +class_definition = class_prefixes + class_specifier; + +clock_clause = decoration[0:1] + E"Clock" + IDENT + E"=" + expression + comment; +sub_partition = E"subpartition" + E"(" + argument_list + E")" + string_comment + (annotation_comment + E";")[0:1] + (Star(E"equation" + ((equation + E";"))) | E"algorithm" + Star(statement + E";")); +base_partition.matcher = E"partition" + string_comment + (annotation_comment + E";")[0:1] + (clock_clause + E";") + sub_partition; -""" -Parses a string in to a BaseModelicaPackage. -""" -function parse_str(data) - parse_package_str(data) -end -""" -Takes a path to a file and parses the contents in to a BaseModelicaPackage. -""" -function parse_file(file) - parse_str(read(file, String)) -end -function display_machine(m::Automa.Machine) - open("/tmp/machine.dot", "w") do io - println(io, Automa.machine2dot(m)) +#equations + +relation = arithmetic_expression + (relational_operator + arithmetic_expression)[0:1]; +logical_factor = E"not"[0:1] + relation; +logical_term = logical_factor + Star(E"and" + logical_factor); +logical_expression = logical_term + Star(E"or" + logical_term); +simple_expression = logical_expression + (E":" + logical_expression + (E":" + logical_expression)[0:1])[0:1]; + +priority = expression; + + +prioritize_equation = E"prioritize" + E"(" + component_reference + E"," + priority + E")"; +prioritize_expression.matcher = E"prioritize" + E"(" + expression + E"," + priority + E")"; + + +initial_equation.matcher = equation | prioritize_equation; + +output_expression_list.matcher = expression[0:1] + Star(E"," + expression[0:1]); +expression_list.matcher = expression + Star(E"," + expression); + +if_expression = Delayed() +expression_no_decoration = simple_expression | if_expression; +if_expression.matcher = + E"if" + expression_no_decoration + E"then" + expression_no_decoration + + Star(E"elseif" + expression_no_decoration + E"then" + expression_no_decoration) + + E"else" + expression_no_decoration; + +expression.matcher = expression_no_decoration + decoration[0:1]; + +if_equation = + E"if" + expression + E"then" + NL + + Star(equation + E";") + + Star(E"elseif" + expression + E"then" + NL + + Star(equation + E";") + ) + + (E"else" + NL + + Star(equation + E";") + )[0:1] + NL + + E"end if"; + +for_index = IDENT + E"in" + expression; + +for_equation = + E"for" + for_index + E"loop" + NL + + Star(equation + E";") + NL + + E"end for"; + +for_statement = + E"for" + for_index + E"loop" + NL + + Star(statement + E";") + NL + + E"end for"; + +while_statement = + E"while" + expression + E"loop" + + Star(statement + E";") + NL + + E"end while"; + +when_equation = + E"when" + expression + E"then" + NL + + Star(statement + E";") + NL + + E"end when"; + +when_statement = + E"when" + expression + E"then" + NL + + Star(statement + E";") + NL + + Star(E"elsewhen" + expression + E"then" + NL + + Star(statement + E";")) + NL + + E"end when"; + +if_statement = + E"if" + expression + E"then" + NL + + Star(statement + E";") + NL + + Star(E"elseif" + expression + E"then" + NL + + Star(statement + E";")) + + (E"else" + NL + + Star(statement + E";"))[0:1] + NL + + E"end if"; + +statement.matcher = decoration[0:1] + (component_reference + (E":=" + expression | function_call_args) | + E"(" + output_expression_list + E")" + E":=" + component_reference + function_call_args | + E"break" | + E"return" | + if_statement | + for_statement | + while_statement | + when_statement) + comment; + +equation.matcher = decoration[0:1] + (simple_expression + decoration[0:1] + (e"=" + expression)[0:1] | + if_equation | + for_equation | + when_equation) + comment; + +end; + +function create_component(prefix, type, components) + #only do parameters and Reals for now + #eventually will need to do arbitrary base modelica types + comp = components[1] #for now only supports one parameter/variable per statement, no "component-list"s + if isempty(prefix) + type = type + name = components[1] + value = components[2] + description = components[3] + return BaseModelicaVariable(type,name,nothing,description) + elseif prefix[1] == "parameter" # only do parameters and Reals for now + type = type + name = comp[1] + value = comp[2] + length(comp) == 3 ? description = comp[3] : description = nothing + return BaseModelicaParameter(type,name,value,description) + elseif prefix[1] == "input" || prefix[1] == "output" + type = type + name = comp[1] + length(comp) == 2 ? description = comp[2] : description = description = nothing + return BaseModelicaVariable(type,name,prefix[1],description) end - run(pipeline(`dot -Tsvg /tmp/machine.dot`, stdout = "/tmp/machine.svg")) - run(`firefox /tmp/machine.svg`) end + +function create_equation(equation_list) + equal_index = findfirst(x -> x == "=", equation_list) + if !isnothing(equal_index) + lhs = equation_list[begin:(equal_index -1)] + rhs = equation_list[(equal_index+1):(end - 1)] # + end +end \ No newline at end of file diff --git a/src/scratch.jl b/src/scratch.jl new file mode 100644 index 0000000..852e105 --- /dev/null +++ b/src/scratch.jl @@ -0,0 +1,36 @@ +parse_one("\n", NL) +parse_one("h", NONDIGIT) +parse_one("4",DIGIT) +parse_one("342534",UNSIGNED_INTEGER) +parse_one("8",Q_CHAR) +parse_one("'eepers_jeepers'",Q_IDENT) +parse_one("oopsers",IDENT) +parse_one("'eepers_jeepers'",IDENT) +parse_one("28342.532842e45",UNSIGNED_NUMBER) +parse_one("\" eepers jeepers peeper \"", STRING) +parse_one("joop.jeep",name) + +parse_one("parameter input", type_prefix) +parse_one("parameter",type_prefix) +parse_one("parameter Real 'juice' \"juice\";", component_clause) + +parse_one("output Real 'juice'",component_clause) +parse_one("Real 'juice' \"the real juice\"",component_clause) +parse_one("parameter Real 'juice' = 45,'juicier' = 56;",Trace(component_clause)) +parse_one("parameter output Real 'juice' = 49 \"description for juice 49\";",component_clause) +parse_dbg("parameter output Real 'juice' = 5;",Trace(component_clause)) + +parse_one("'locomotive' + 'doopers' = 'jeepers' \"holy creepers\";", equation) +parse_one("'locomotive' + 'loco'", equation) + + +parse_dbg("Real 'juicier' ;", component_clause) + +parse_one("x*y",term) + +parse_one("parameter", type_prefix) + +parse_one("3+ 5 -3/ 4*5+787 -10", arithmetic_expression) + +eeps = e"eeps" + e"peeps" +parse_one("abeepspeepseepspeeps", e"a" + e"b" + eeps[1:end,:&]) \ No newline at end of file From b0fe653ed6e9045b62ce179830d2b1fc98b3a26e Mon Sep 17 00:00:00 2001 From: jClugstor Date: Wed, 6 Mar 2024 13:40:01 -0500 Subject: [PATCH 2/6] Parameter and variable object parsing. Compositions work --- src/parser.jl | 94 ++++++++++++++++++++++++++++++-------------------- src/scratch.jl | 10 +++--- 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/src/parser.jl b/src/parser.jl index 037e437..5381774 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -3,6 +3,58 @@ using ParserCombinator list2string(x) = reduce(*,x) spc = Drop(Star(Space())) +function create_component(prefix, type, components) + #only do parameters and Reals for now + #eventually will need to do arbitrary base modelica types + + comp = components[1] #for now only supports one parameter/variable per statement, no "component-list"s + if isempty(prefix) + type = type + name = comp[1] + length(comp) == 2 ? description = comp[2] : description = description = nothing + return BaseModelicaVariable(type,name,nothing,description) + elseif prefix[1] == "parameter" # only do parameters and Reals for now + type = type + name = comp[1] + value = comp[2] + length(comp) == 3 ? description = comp[3] : description = nothing + return BaseModelicaParameter(type,name,value,description) + elseif prefix[1] == "input" || prefix[1] == "output" + type = type + name = comp[1] + length(comp) == 2 ? description = comp[2] : description = description = nothing + return BaseModelicaVariable(type,name,prefix[1],description) + end +end + +function create_equation(equation_list) + #so far only handles normal equations, no if, whens, or anything like that + eq = equation_list[1] + equal_index = findfirst(x -> x == "=", eq) + println(equal_index) + if !isnothing(equal_index) + lhs = eq[begin:(equal_index -1)] + rhs = eq[(equal_index+1):end] # + end + !isempty(equation_list[2]) ? description = only(equation_list[2]) : description = "" + BaseModelicaEquation(lhs,rhs,description) +end + +function create_initial_equation(equation_list) + eq = equation_list[1] + equal_index = findfirst(x -> x == "=", eq) + println(equal_index) + if !isnothing(equal_index) + lhs = eq[begin:(equal_index -1)] + rhs = eq[(equal_index+1):end] # + end + !isempty(equation_list[2]) ? description = only(equation_list[2]) : description = "" + BaseModelicaInitialEquation(lhs,rhs,description) +end + + +spc = Drop(Star(Space())) + # Base Modelica grammar @with_names begin NL = p"\r\n" | p"\n" | p"\r"; @@ -107,7 +159,7 @@ equation = Delayed() initial_equation = Delayed() statement = Delayed() base_partition = Delayed() -composition = Star(decoration[0:1] + generic_element + E";") + +composition = Star(decoration[0:1] + generic_element + E";" + spc) + Star((e"equation" + Star(equation + E";")) | (e"initial equation" + Star(initial_equation + E";")) | (e"initial"[0:1] + e"algorithm" + Star(statement + E";"))) + (decoration[0:1] + E"external" + language_specification[0:1] + external_function_call[0:1] + annotation_comment[0:1] + E";")[0:1] + @@ -115,7 +167,7 @@ composition = Star(decoration[0:1] + generic_element + E";") + base_prefix = e"input" | e"output" -long_class_specifier = IDENT + string_comment + composition + E"end"; +long_class_specifier = IDENT + string_comment + composition + E"end" + IDENT; short_class_specifier = IDENT + E"=" + (base_prefix[0:1] + type_specifier + class_modification[0:1]) | (e"enumeration" + E"(" + (enum_list[0:1] | E":" ) + E")") + comment; class_prefixes = e"type" | e"record" | ((e"pure constant")[0:1] | e"impure")[0:1] + e"function"; @@ -216,41 +268,9 @@ statement.matcher = decoration[0:1] + (component_reference + (E":=" + expression while_statement | when_statement) + comment; -equation.matcher = decoration[0:1] + (simple_expression + decoration[0:1] + (e"=" + expression)[0:1] | +equation.matcher = ((decoration[0:1] + (simple_expression + decoration[0:1] + (e"=" + expression)[0:1]) | if_equation | for_equation | - when_equation) + comment; - -end; - -function create_component(prefix, type, components) - #only do parameters and Reals for now - #eventually will need to do arbitrary base modelica types - comp = components[1] #for now only supports one parameter/variable per statement, no "component-list"s - if isempty(prefix) - type = type - name = components[1] - value = components[2] - description = components[3] - return BaseModelicaVariable(type,name,nothing,description) - elseif prefix[1] == "parameter" # only do parameters and Reals for now - type = type - name = comp[1] - value = comp[2] - length(comp) == 3 ? description = comp[3] : description = nothing - return BaseModelicaParameter(type,name,value,description) - elseif prefix[1] == "input" || prefix[1] == "output" - type = type - name = comp[1] - length(comp) == 2 ? description = comp[2] : description = description = nothing - return BaseModelicaVariable(type,name,prefix[1],description) - end -end + when_equation) & comment) |> create_equation; -function create_equation(equation_list) - equal_index = findfirst(x -> x == "=", equation_list) - if !isnothing(equal_index) - lhs = equation_list[begin:(equal_index -1)] - rhs = equation_list[(equal_index+1):(end - 1)] # - end -end \ No newline at end of file +end; \ No newline at end of file diff --git a/src/scratch.jl b/src/scratch.jl index 852e105..f2d8b4e 100644 --- a/src/scratch.jl +++ b/src/scratch.jl @@ -14,8 +14,8 @@ parse_one("parameter input", type_prefix) parse_one("parameter",type_prefix) parse_one("parameter Real 'juice' \"juice\";", component_clause) -parse_one("output Real 'juice'",component_clause) -parse_one("Real 'juice' \"the real juice\"",component_clause) +parse_one("output Real 'juice' \"juicy\";",component_clause) +parse_one("Real 'juice' \"the real juice\";",component_clause) parse_one("parameter Real 'juice' = 45,'juicier' = 56;",Trace(component_clause)) parse_one("parameter output Real 'juice' = 49 \"description for juice 49\";",component_clause) parse_dbg("parameter output Real 'juice' = 5;",Trace(component_clause)) @@ -23,8 +23,10 @@ parse_dbg("parameter output Real 'juice' = 5;",Trace(component_clause)) parse_one("'locomotive' + 'doopers' = 'jeepers' \"holy creepers\";", equation) parse_one("'locomotive' + 'loco'", equation) - -parse_dbg("Real 'juicier' ;", component_clause) +parse_one("parameter Real 'juice' = 34;", generic_element) +parse_one("Real 'juice' \"juicy\";",generic_element) +parse_one("parameter Real 'dop' = 6; Real 'juice' \"juicy\"; \n Real 'fruit' \"fruity\"; \n parameter Real 'doop' = 60;",composition) +parse_one("parameter Real 'juice' = 34;", component_clause) parse_one("x*y",term) From ae9d31fa91395d5c177cbf2ead98f176aae73514 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 8 Mar 2024 15:20:05 -0500 Subject: [PATCH 3/6] Very close to a working parser --- src/parser.jl | 76 +++++++++++++++++++++++++++++++++----------------- src/scratch.jl | 65 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/src/parser.jl b/src/parser.jl index 5381774..f39046c 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -1,22 +1,24 @@ using ParserCombinator -list2string(x) = reduce(*,x) +list2string(x) = isempty(x) ? x : reduce(*,x) spc = Drop(Star(Space())) function create_component(prefix, type, components) #only do parameters and Reals for now #eventually will need to do arbitrary base modelica types - + #println("prefix: $prefix") + #rintln("type: $type") + #println("components: $components") comp = components[1] #for now only supports one parameter/variable per statement, no "component-list"s if isempty(prefix) type = type name = comp[1] - length(comp) == 2 ? description = comp[2] : description = description = nothing + length(comp) == 2 ? description = comp[2] : description = nothing return BaseModelicaVariable(type,name,nothing,description) elseif prefix[1] == "parameter" # only do parameters and Reals for now type = type name = comp[1] - value = comp[2] + length(comp) > 1 ? value = comp[2] : value = nothing length(comp) == 3 ? description = comp[3] : description = nothing return BaseModelicaParameter(type,name,value,description) elseif prefix[1] == "input" || prefix[1] == "output" @@ -29,31 +31,47 @@ end function create_equation(equation_list) #so far only handles normal equations, no if, whens, or anything like that + #println(equation_list) eq = equation_list[1] equal_index = findfirst(x -> x == "=", eq) - println(equal_index) if !isnothing(equal_index) - lhs = eq[begin:(equal_index -1)] + lhs = eq[begin:(equal_index-1)] rhs = eq[(equal_index+1):end] # + else + lhs = eq # hack because equations don't need to be equations in base modelica for some reason + rhs = "" end !isempty(equation_list[2]) ? description = only(equation_list[2]) : description = "" BaseModelicaEquation(lhs,rhs,description) end function create_initial_equation(equation_list) + #so far only handles normal equations, no if, whens, or anything like that eq = equation_list[1] equal_index = findfirst(x -> x == "=", eq) - println(equal_index) if !isnothing(equal_index) - lhs = eq[begin:(equal_index -1)] + lhs = eq[begin:(equal_index-1)] rhs = eq[(equal_index+1):end] # + else + lhs = eq # hack because equations don't need to be equations in base modelica + rhs = "" end !isempty(equation_list[2]) ? description = only(equation_list[2]) : description = "" BaseModelicaInitialEquation(lhs,rhs,description) end +function construct_package(input) + variable = [] + parameters = [] + equations = [] + for thing in input + typeof(thing) == BaseModelicaVariable ? push!(variables, thing): + typeof(thing) == BaseModelicaParameter ? push!(parameters, thing): + typeof(thing) == BaseModelicaEquation ? push!(equations, thing): + nothing + end +end -spc = Drop(Star(Space())) # Base Modelica grammar @with_names begin @@ -66,14 +84,14 @@ ML_COMMENT = p"/[*]([^*]|([*][^/]))*[*]/"; NONDIGIT = p"_|[a-z]|[A-Z]"; DIGIT = p"[0-9]"; UNSIGNED_INTEGER = Plus(DIGIT); -Q_CHAR = NONDIGIT | DIGIT | p"[-!#$%&()*>+,./:;<>=?>@[]{}|~ ^]"; +Q_CHAR = NONDIGIT | DIGIT | p"[-!#$%&()*>+,./:;<>=?>@\[\]{}|~ ^]"; S_ESCAPE = p"\\['\"?\\abfnrtv]"; S_CHAR = NL | p"[^\r\n\\\"]"; -Q_IDENT = E"'" + (Q_CHAR | S_ESCAPE ) + Star(Q_CHAR | S_ESCAPE | E"\"" ) + E"'"; -IDENT = ((NONDIGIT + Star( DIGIT | NONDIGIT )) | Q_IDENT) |> list2string; +Q_IDENT = (E"'" + (Q_CHAR | S_ESCAPE ) + Star(Q_CHAR | S_ESCAPE | E"\"" ) + E"'") |> list2string; +IDENT = (((NONDIGIT + Star( DIGIT | NONDIGIT )) |> list2string) | Q_IDENT); STRING = e"\"" + Star( S_CHAR | S_ESCAPE ) + e"\"" |> list2string; EXPONENT = ( e"e" | e"E" ) + ( e"+" | e"-" )[0:1] + DIGIT[1:end]; -UNSIGNED_NUMBER = DIGIT[1:end] + ( e"." + Star(DIGIT) )[0:1] + EXPONENT[0:1] |> (x -> parse(Float64, reduce(*,x))); +UNSIGNED_NUMBER = DIGIT[1:end] + ( e"." + Star(DIGIT) )[0:1] + EXPONENT[0:1] |> list2string; #component clauses name = (IDENT + Star(e"." + IDENT)) |> list2string; @@ -109,7 +127,7 @@ mul_operator = e"*" | e"/" | e".*" | e"./"; for_index = IDENT + E"in" + expression; named_arguments = Delayed() -function_partial_application = E"function" + type_specifier + E"(" + named_arguments + E")"; +function_partial_application = E"function" + type_specifier + e"(" + named_arguments + e")"; function_argument = function_partial_application | expression; function_arguments_non_first = Delayed() function_arguments_non_first.matcher = (function_argument + (E"," + function_arguments_non_first)[0:1]) | named_arguments; @@ -126,9 +144,9 @@ array_arguments = expression + (Star(E"," + expression) | E"for" + for_index); primary = UNSIGNED_NUMBER | STRING | e"false" | e"true" | ((e"der" | e"initial" | e"pure") + function_call_args) | (component_reference + function_call_args[0:1]) | - (E"(" + output_expression_list + E")" + array_subscripts[0:1]) | - (E"[" + expression_list + Star(E";" + expression_list) + E"]") | - (E"{" + array_arguments + E"}") | + (e"(" + output_expression_list + e")" + array_subscripts[0:1]) | + (e"[" + expression_list + Star(E";" + expression_list) + e"]") | + (e"{" + array_arguments + e"}") | E"end"; factor = primary + ((E"^" | E".^") + primary)[0:1]; term = factor + spc + Star(mul_operator + spc + factor); @@ -159,18 +177,18 @@ equation = Delayed() initial_equation = Delayed() statement = Delayed() base_partition = Delayed() -composition = Star(decoration[0:1] + generic_element + E";" + spc) + - Star((e"equation" + Star(equation + E";")) | - (e"initial equation" + Star(initial_equation + E";")) | +composition = Star(decoration[0:1] + generic_element + E";" + spc) + + Star((spc + e"equation" + spc + Star(spc + equation + E";" + spc)) | + (e"initial equation" + spc + Star(spc + initial_equation + E";" + spc)) | (e"initial"[0:1] + e"algorithm" + Star(statement + E";"))) + (decoration[0:1] + E"external" + language_specification[0:1] + external_function_call[0:1] + annotation_comment[0:1] + E";")[0:1] + Star(base_partition) + (annotation_comment + E";")[0:1]; base_prefix = e"input" | e"output" -long_class_specifier = IDENT + string_comment + composition + E"end" + IDENT; +long_class_specifier = IDENT + spc + string_comment + spc + composition + spc + e"end" + spc + IDENT; short_class_specifier = IDENT + E"=" + (base_prefix[0:1] + type_specifier + class_modification[0:1]) | (e"enumeration" + E"(" + (enum_list[0:1] | E":" ) + E")") + comment; -class_prefixes = e"type" | e"record" | ((e"pure constant")[0:1] | e"impure")[0:1] + e"function"; +class_prefixes = e"type" | e"record" | ((e"pure constant")[0:1] | (e"impure")[0:1]) + e"function"; der_class_specifier = IDENT + E"=" + E" "[0:1] + E"der" + E" " + E"(" + type_specifier + E"," + IDENT + Star(E"," + IDENT) + E")" + comment; class_specifier = long_class_specifier | short_class_specifier | der_class_specifier; class_definition = class_prefixes + class_specifier; @@ -183,7 +201,7 @@ base_partition.matcher = E"partition" + string_comment + (annotation_comment + E #equations -relation = arithmetic_expression + (relational_operator + arithmetic_expression)[0:1]; +relation = arithmetic_expression + (relational_operator + arithmetic_expression)[0:1] |> list2string; logical_factor = E"not"[0:1] + relation; logical_term = logical_factor + Star(E"and" + logical_factor); logical_expression = logical_term + Star(E"or" + logical_term); @@ -196,7 +214,7 @@ prioritize_equation = E"prioritize" + E"(" + component_reference + E"," + priori prioritize_expression.matcher = E"prioritize" + E"(" + expression + E"," + priority + E")"; -initial_equation.matcher = equation | prioritize_equation; +initial_equation.matcher = (equation | prioritize_equation) |> create_initial_equation; output_expression_list.matcher = expression[0:1] + Star(E"," + expression[0:1]); expression_list.matcher = expression + Star(E"," + expression); @@ -268,9 +286,17 @@ statement.matcher = decoration[0:1] + (component_reference + (E":=" + expression while_statement | when_statement) + comment; -equation.matcher = ((decoration[0:1] + (simple_expression + decoration[0:1] + (e"=" + expression)[0:1]) | +equation.matcher = ((decoration[0:1] + (simple_expression + decoration[0:1] + (spc + e"=" + spc + expression)[0:1]) | if_equation | for_equation | when_equation) & comment) |> create_equation; +base_modelica = + (spc + E"package" + spc + IDENT + spc + + Star((decoration[0:1] + spc + class_definition + spc + E";") | + (decoration[0:1] + global_constant + E";")) + + spc + decoration[0:1] + spc + e"model" + spc + long_class_specifier + E";" + + spc + (annotation_comment + E";")[0:1] + spc + + e"end" + spc + IDENT + spc + E";" + spc) |> construct_package + end; \ No newline at end of file diff --git a/src/scratch.jl b/src/scratch.jl index f2d8b4e..5dc975f 100644 --- a/src/scratch.jl +++ b/src/scratch.jl @@ -35,4 +35,67 @@ parse_one("parameter", type_prefix) parse_one("3+ 5 -3/ 4*5+787 -10", arithmetic_expression) eeps = e"eeps" + e"peeps" -parse_one("abeepspeepseepspeeps", e"a" + e"b" + eeps[1:end,:&]) \ No newline at end of file +parse_one("abeepspeepseepspeeps", e"a" + e"b" + eeps[1:end,:&]) + +parse_one("3+ ( 4 - 32 ) / 2 = 3", equation) + +parse_one(""" +Real 'juice.juice' \"juicy\"; +Real 'fruit' \"fruity\"; +output Real 'output_fruit'; +parameter Real 'doop' = 60; +equation +('juice'+'fruit')*'blade' = 'puree'; +'juicy' * 'fruit' = 'juicyfruit'; +""", composition) + +parse_one("""JuiceModel +Real 'juice.juice' \"juicy\"; +Real 'fruit' \"fruity\"; +output Real 'output_fruit'; +parameter Real 'doop' = 60; +equation +('juice'+'fruit')*'blade' = 'puree'; +'juicy' * 'fruit' = 'juicyfruit'; +end JuiceModel; +""", long_class_specifier, debug = true) + +parse_one(""" +package JuiceModel + model JuiceModel + Real 'juice.juice' \"juicy\"; + Real 'fruit' \"fruity\"; + output Real 'output_fruit'; + parameter Real 'doop' = 60; + equation + ('juice'+'fruit' + 100.0)*'blade' = 'puree'; + 'juicy' * 'fruit' = 'juicyfruit'; + end JuiceModel; +end JuiceModel; +end JuiceModel;""" +,base_modelica) + +parse_one(""" +package JuiceModel + model JuiceModel + end JuiceModel; +end JuiceModel;""",base_modelica) + + +parse_one("juice +end juice;", long_class_specifier) + +parse_one("('juice' + 'juice2')/2", relation) + +parse_one("'juice'= 4;",composition) + +parse_one("""initial equation +'juice' = 4;""",composition) + +parse_lines("""equation +'juice' = 'juice_right'; +'drink' = 'drink';""",composition) + +parse_one("'juice.juice'",name) + +parse_one(".",Q_CHAR) From 41b8cd5a01619730c181b55a036aeaaab8510e0e Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 8 Mar 2024 16:15:14 -0500 Subject: [PATCH 4/6] Initial equations work --- src/parser.jl | 33 ++++++++++++++++----------------- src/scratch.jl | 6 ++++-- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/parser.jl b/src/parser.jl index f39046c..33e6813 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -24,7 +24,7 @@ function create_component(prefix, type, components) elseif prefix[1] == "input" || prefix[1] == "output" type = type name = comp[1] - length(comp) == 2 ? description = comp[2] : description = description = nothing + length(comp) == 2 ? description = comp[2] : description = nothing return BaseModelicaVariable(type,name,prefix[1],description) end end @@ -45,31 +45,30 @@ function create_equation(equation_list) BaseModelicaEquation(lhs,rhs,description) end -function create_initial_equation(equation_list) +function create_initial_equation(equation) #so far only handles normal equations, no if, whens, or anything like that - eq = equation_list[1] - equal_index = findfirst(x -> x == "=", eq) - if !isnothing(equal_index) - lhs = eq[begin:(equal_index-1)] - rhs = eq[(equal_index+1):end] # - else - lhs = eq # hack because equations don't need to be equations in base modelica - rhs = "" - end - !isempty(equation_list[2]) ? description = only(equation_list[2]) : description = "" - BaseModelicaInitialEquation(lhs,rhs,description) + eq = equation[1] + lhs = eq.lhs + rhs = eq.rhs + BaseModelicaInitialEquation(lhs,rhs,nothing) end function construct_package(input) - variable = [] + variables = [] parameters = [] equations = [] + initial_equations = [] for thing in input - typeof(thing) == BaseModelicaVariable ? push!(variables, thing): - typeof(thing) == BaseModelicaParameter ? push!(parameters, thing): - typeof(thing) == BaseModelicaEquation ? push!(equations, thing): + typeof(thing) == BaseModelicaVariable ? push!(variables, thing) : + typeof(thing) == BaseModelicaParameter ? push!(parameters, thing) : + typeof(thing) == BaseModelicaEquation ? push!(equations, thing) : + typeof(thing) == BaseModelicaInitialEquation ? push!(initial_equations,thing) : nothing end + + BaseModelicaModel(nothing,nothing,parameters,variables,equations,initial_equations) + + end diff --git a/src/scratch.jl b/src/scratch.jl index 5dc975f..9c4efd1 100644 --- a/src/scratch.jl +++ b/src/scratch.jl @@ -60,7 +60,7 @@ equation end JuiceModel; """, long_class_specifier, debug = true) -parse_one(""" +parse_dbg(""" package JuiceModel model JuiceModel Real 'juice.juice' \"juicy\"; @@ -70,10 +70,12 @@ package JuiceModel equation ('juice'+'fruit' + 100.0)*'blade' = 'puree'; 'juicy' * 'fruit' = 'juicyfruit'; + initial equation + 'juicy' = 1000; end JuiceModel; end JuiceModel; end JuiceModel;""" -,base_modelica) +,Trace(base_modelica)) parse_one(""" package JuiceModel From b923febb1b01c5d722057aaf52d333234d561e18 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 11 Mar 2024 11:39:36 -0400 Subject: [PATCH 5/6] fixed function args, so der and other functions are parsed --- src/parser.jl | 31 ++++++++++++++----------------- src/scratch.jl | 45 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/parser.jl b/src/parser.jl index 33e6813..1a16c91 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -6,9 +6,6 @@ spc = Drop(Star(Space())) function create_component(prefix, type, components) #only do parameters and Reals for now #eventually will need to do arbitrary base modelica types - #println("prefix: $prefix") - #rintln("type: $type") - #println("components: $components") comp = components[1] #for now only supports one parameter/variable per statement, no "component-list"s if isempty(prefix) type = type @@ -31,12 +28,12 @@ end function create_equation(equation_list) #so far only handles normal equations, no if, whens, or anything like that - #println(equation_list) + #println(equation_list) eq = equation_list[1] equal_index = findfirst(x -> x == "=", eq) if !isnothing(equal_index) - lhs = eq[begin:(equal_index-1)] - rhs = eq[(equal_index+1):end] # + lhs = only(eq[begin:(equal_index-1)]) + rhs = only(eq[(equal_index+1):end]) # else lhs = eq # hack because equations don't need to be equations in base modelica for some reason rhs = "" @@ -133,21 +130,21 @@ function_arguments_non_first.matcher = (function_argument + (E"," + function_arg named_argument = IDENT + E"=" + function_argument; named_arguments.matcher = named_argument + Star(E"," + named_argument); function_partial_applications = E"function" + type_specifier + E"(" + named_arguments[0:1] + E")"; -function_arguments = (expression + (E"," + function_arguments_non_first) | (E"for" + for_index)[0:1]) | +function_arguments = (expression + ((E"," + function_arguments_non_first) | (E"for" + for_index))[0:1]) | (function_partial_application + (E"," + function_arguments_non_first)[0:1]) | named_arguments; -function_call_args = E"(" + function_arguments[0:1] + E")"; +function_call_args = e"(" + function_arguments[0:1] + e")"; output_expression_list = Delayed() expression_list = Delayed() array_arguments = expression + (Star(E"," + expression) | E"for" + for_index); primary = UNSIGNED_NUMBER | STRING | e"false" | e"true" | ((e"der" | e"initial" | e"pure") + function_call_args) | (component_reference + function_call_args[0:1]) | - (e"(" + output_expression_list + e")" + array_subscripts[0:1]) | - (e"[" + expression_list + Star(E";" + expression_list) + e"]") | - (e"{" + array_arguments + e"}") | + (e"(" + spc + output_expression_list + spc + e")" + array_subscripts[0:1]) | + (e"[" + spc + expression_list + spc +Star(E";" + spc + expression_list) + spc + e"]") | + (e"{" + spc + array_arguments + spc + e"}") | E"end"; -factor = primary + ((E"^" | E".^") + primary)[0:1]; +factor = primary + spc + ((E"^" | E".^") + spc + primary)[0:1]; term = factor + spc + Star(mul_operator + spc + factor); arithmetic_expression = add_operator[0:1] + spc + term + spc + Star(add_operator + spc + term); @@ -200,11 +197,11 @@ base_partition.matcher = E"partition" + string_comment + (annotation_comment + E #equations -relation = arithmetic_expression + (relational_operator + arithmetic_expression)[0:1] |> list2string; -logical_factor = E"not"[0:1] + relation; -logical_term = logical_factor + Star(E"and" + logical_factor); -logical_expression = logical_term + Star(E"or" + logical_term); -simple_expression = logical_expression + (E":" + logical_expression + (E":" + logical_expression)[0:1])[0:1]; +relation = arithmetic_expression + spc + (relational_operator + arithmetic_expression)[0:1] |> list2string; +logical_factor = E"not"[0:1] + spc + relation; +logical_term = logical_factor + spc + Star(E"and" + spc + logical_factor); +logical_expression = logical_term + spc + Star(E"or" + spc + logical_term); +simple_expression = logical_expression + spc + (E":" + spc +logical_expression + spc + (E":" + spc + logical_expression)[0:1])[0:1]; priority = expression; diff --git a/src/scratch.jl b/src/scratch.jl index 9c4efd1..9ab9734 100644 --- a/src/scratch.jl +++ b/src/scratch.jl @@ -60,28 +60,55 @@ equation end JuiceModel; """, long_class_specifier, debug = true) -parse_dbg(""" +parse_one(""" +package JuiceModel + model JuiceModel + end JuiceModel; +end JuiceModel;""",base_modelica) + +pkg = parse_dbg(""" package JuiceModel model JuiceModel Real 'juice.juice' \"juicy\"; Real 'fruit' \"fruity\"; + Real 'T'; output Real 'output_fruit'; + parameter Real 'mope' = 50; parameter Real 'doop' = 60; + parameter Real 'soap' = 59; + parameter Real 'moap' = 200; + parameter Real 'joke' = 23432; + parameter Real 'choke' = 2343; equation - ('juice'+'fruit' + 100.0)*'blade' = 'puree'; + ('juice'+'fruit' + 100.0)*'blade' = 'puree' "description"; 'juicy' * 'fruit' = 'juicyfruit'; initial equation 'juicy' = 1000; + 'fruit' = 2000; end JuiceModel; end JuiceModel; end JuiceModel;""" -,Trace(base_modelica)) - -parse_one(""" -package JuiceModel - model JuiceModel - end JuiceModel; -end JuiceModel;""",base_modelica) +,base_modelica) + +parse_dbg("""package 'NewtonCoolingWithDefaults' + model 'NewtonCoolingWithDefaults' "Cooling example with default parameter values" + parameter Real 'T_inf' = 25.0 "Ambient temperature"; + parameter Real 'T0' = 90.0 "Initial temperature"; + parameter Real 'h' = 0.7 "Convective cooling coefficient"; + parameter Real 'A' = 1.0 "Surface area"; + parameter Real 'm' = 0.1 "Mass of thermal capacitance"; + parameter Real 'c_p' = 1.2 "Specific heat"; + Real 'T' "Temperature"; + initial equation + 'T' = 'T0' "Specify initial value for T"; + equation + 'm' * 'c_p' * der('T') = 'h' * 'A' * ('T_inf' - 'T') "Newton's law of cooling"; + end 'NewtonCoolingWithDefaults'; +end 'NewtonCoolingWithDefaults';""",base_modelica) + +parse_one("'m' * 'c_p' * der('T') = 'h' * 'A' * ('T_inf' - 'T')",equation) +parse_one("der('T')",equation) +parse_one("'T'",expression) parse_one("juice From e981e01d5f3550bfcd79fb288d099c07c35d2ed4 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 11 Mar 2024 14:07:09 -0400 Subject: [PATCH 6/6] Tests run, new parser works on Newton cooling model --- Project.toml | 1 + src/BaseModelica.jl | 4 +- src/parser.jl | 26 ++++++--- src/scratch.jl | 130 -------------------------------------------- test/runtests.jl | 56 +++++++++++++++++-- 5 files changed, 74 insertions(+), 143 deletions(-) delete mode 100644 src/scratch.jl diff --git a/Project.toml b/Project.toml index ec03f83..a112350 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,7 @@ ModelingToolkit = "8.75, 9" SafeTestsets = "0.1" Test = "1.10" julia = "1.10" +ParserCombinator = "2" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" diff --git a/src/BaseModelica.jl b/src/BaseModelica.jl index c5d4b77..fd32553 100644 --- a/src/BaseModelica.jl +++ b/src/BaseModelica.jl @@ -68,8 +68,8 @@ parse_basemodelica("testfiles/NewtonCoolingBase.mo") ``` """ function parse_basemodelica(filename::String) - model = parse_file(filename) - baseModelica_to_ModelingToolkit(model.model) + package = parse_file(filename) + baseModelica_to_ModelingToolkit(package.model) end export parse_basemodelica diff --git a/src/parser.jl b/src/parser.jl index 1a16c91..1d3b23e 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -1,5 +1,3 @@ -using ParserCombinator - list2string(x) = isempty(x) ? x : reduce(*,x) spc = Drop(Star(Space())) @@ -51,6 +49,8 @@ function create_initial_equation(equation) end function construct_package(input) + name = input[1] + input[4] isa String ? description = input[4] : description = nothing variables = [] parameters = [] equations = [] @@ -63,9 +63,8 @@ function construct_package(input) nothing end - BaseModelicaModel(nothing,nothing,parameters,variables,equations,initial_equations) - - + model = BaseModelicaModel(name,description,parameters,variables,equations,initial_equations) + BaseModelicaPackage(name, model) end @@ -85,7 +84,7 @@ S_ESCAPE = p"\\['\"?\\abfnrtv]"; S_CHAR = NL | p"[^\r\n\\\"]"; Q_IDENT = (E"'" + (Q_CHAR | S_ESCAPE ) + Star(Q_CHAR | S_ESCAPE | E"\"" ) + E"'") |> list2string; IDENT = (((NONDIGIT + Star( DIGIT | NONDIGIT )) |> list2string) | Q_IDENT); -STRING = e"\"" + Star( S_CHAR | S_ESCAPE ) + e"\"" |> list2string; +STRING = E"\"" + Star( S_CHAR | S_ESCAPE ) + E"\"" |> list2string; EXPONENT = ( e"e" | e"E" ) + ( e"+" | e"-" )[0:1] + DIGIT[1:end]; UNSIGNED_NUMBER = DIGIT[1:end] + ( e"." + Star(DIGIT) )[0:1] + EXPONENT[0:1] |> list2string; @@ -294,5 +293,18 @@ base_modelica = spc + decoration[0:1] + spc + e"model" + spc + long_class_specifier + E";" + spc + (annotation_comment + E";")[0:1] + spc + e"end" + spc + IDENT + spc + E";" + spc) |> construct_package +end; + +""" +Parses a String in to a BaseModelicaPackage. +""" +function parse_str(data) + only(parse_one(data,base_modelica)) +end -end; \ No newline at end of file +""" +Takes a path to a file and parses the contents in to a BaseModelicaPackage +""" +function parse_file(file) + parse_str(read(file,String)) +end \ No newline at end of file diff --git a/src/scratch.jl b/src/scratch.jl deleted file mode 100644 index 9ab9734..0000000 --- a/src/scratch.jl +++ /dev/null @@ -1,130 +0,0 @@ -parse_one("\n", NL) -parse_one("h", NONDIGIT) -parse_one("4",DIGIT) -parse_one("342534",UNSIGNED_INTEGER) -parse_one("8",Q_CHAR) -parse_one("'eepers_jeepers'",Q_IDENT) -parse_one("oopsers",IDENT) -parse_one("'eepers_jeepers'",IDENT) -parse_one("28342.532842e45",UNSIGNED_NUMBER) -parse_one("\" eepers jeepers peeper \"", STRING) -parse_one("joop.jeep",name) - -parse_one("parameter input", type_prefix) -parse_one("parameter",type_prefix) -parse_one("parameter Real 'juice' \"juice\";", component_clause) - -parse_one("output Real 'juice' \"juicy\";",component_clause) -parse_one("Real 'juice' \"the real juice\";",component_clause) -parse_one("parameter Real 'juice' = 45,'juicier' = 56;",Trace(component_clause)) -parse_one("parameter output Real 'juice' = 49 \"description for juice 49\";",component_clause) -parse_dbg("parameter output Real 'juice' = 5;",Trace(component_clause)) - -parse_one("'locomotive' + 'doopers' = 'jeepers' \"holy creepers\";", equation) -parse_one("'locomotive' + 'loco'", equation) - -parse_one("parameter Real 'juice' = 34;", generic_element) -parse_one("Real 'juice' \"juicy\";",generic_element) -parse_one("parameter Real 'dop' = 6; Real 'juice' \"juicy\"; \n Real 'fruit' \"fruity\"; \n parameter Real 'doop' = 60;",composition) -parse_one("parameter Real 'juice' = 34;", component_clause) - -parse_one("x*y",term) - -parse_one("parameter", type_prefix) - -parse_one("3+ 5 -3/ 4*5+787 -10", arithmetic_expression) - -eeps = e"eeps" + e"peeps" -parse_one("abeepspeepseepspeeps", e"a" + e"b" + eeps[1:end,:&]) - -parse_one("3+ ( 4 - 32 ) / 2 = 3", equation) - -parse_one(""" -Real 'juice.juice' \"juicy\"; -Real 'fruit' \"fruity\"; -output Real 'output_fruit'; -parameter Real 'doop' = 60; -equation -('juice'+'fruit')*'blade' = 'puree'; -'juicy' * 'fruit' = 'juicyfruit'; -""", composition) - -parse_one("""JuiceModel -Real 'juice.juice' \"juicy\"; -Real 'fruit' \"fruity\"; -output Real 'output_fruit'; -parameter Real 'doop' = 60; -equation -('juice'+'fruit')*'blade' = 'puree'; -'juicy' * 'fruit' = 'juicyfruit'; -end JuiceModel; -""", long_class_specifier, debug = true) - -parse_one(""" -package JuiceModel - model JuiceModel - end JuiceModel; -end JuiceModel;""",base_modelica) - -pkg = parse_dbg(""" -package JuiceModel - model JuiceModel - Real 'juice.juice' \"juicy\"; - Real 'fruit' \"fruity\"; - Real 'T'; - output Real 'output_fruit'; - parameter Real 'mope' = 50; - parameter Real 'doop' = 60; - parameter Real 'soap' = 59; - parameter Real 'moap' = 200; - parameter Real 'joke' = 23432; - parameter Real 'choke' = 2343; - equation - ('juice'+'fruit' + 100.0)*'blade' = 'puree' "description"; - 'juicy' * 'fruit' = 'juicyfruit'; - initial equation - 'juicy' = 1000; - 'fruit' = 2000; - end JuiceModel; -end JuiceModel; -end JuiceModel;""" -,base_modelica) - -parse_dbg("""package 'NewtonCoolingWithDefaults' - model 'NewtonCoolingWithDefaults' "Cooling example with default parameter values" - parameter Real 'T_inf' = 25.0 "Ambient temperature"; - parameter Real 'T0' = 90.0 "Initial temperature"; - parameter Real 'h' = 0.7 "Convective cooling coefficient"; - parameter Real 'A' = 1.0 "Surface area"; - parameter Real 'm' = 0.1 "Mass of thermal capacitance"; - parameter Real 'c_p' = 1.2 "Specific heat"; - Real 'T' "Temperature"; - initial equation - 'T' = 'T0' "Specify initial value for T"; - equation - 'm' * 'c_p' * der('T') = 'h' * 'A' * ('T_inf' - 'T') "Newton's law of cooling"; - end 'NewtonCoolingWithDefaults'; -end 'NewtonCoolingWithDefaults';""",base_modelica) - -parse_one("'m' * 'c_p' * der('T') = 'h' * 'A' * ('T_inf' - 'T')",equation) -parse_one("der('T')",equation) -parse_one("'T'",expression) - - -parse_one("juice -end juice;", long_class_specifier) - -parse_one("('juice' + 'juice2')/2", relation) - -parse_one("'juice'= 4;",composition) - -parse_one("""initial equation -'juice' = 4;""",composition) - -parse_lines("""equation -'juice' = 'juice_right'; -'drink' = 'drink';""",composition) - -parse_one("'juice.juice'",name) - -parse_one(".",Q_CHAR) diff --git a/test/runtests.jl b/test/runtests.jl index 060b34a..e48fd5a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,11 +5,59 @@ using Test, SafeTestsets @safetestset "Parsing and Conversion Tests" begin using BaseModelica using ModelingToolkit + BM = BaseModelica + PC = BM.ParserCombinator + + @test PC.parse_one("3+ 5 -3/ 4*5+787 -10", BM.arithmetic_expression) |> length == 13 + @test only(PC.parse_one("output Real 'juice' \"juicy\";",BM.component_clause)) isa BM.BaseModelicaVariable + @test only(PC.parse_one("Real 'juice' \"the real juice\";",BM.component_clause)) isa BM.BaseModelicaVariable + @test only(PC.parse_one("parameter Real 'juice' = 45",BM.component_clause)) isa BM.BaseModelicaParameter + @test only(PC.parse_one("parameter Real 'juice' = 45 \"juice 45\"",BM.component_clause)) isa BM.BaseModelicaParameter + @test only(PC.parse_one("'locomotive' + 'creepers' = 'jeepers' \"holy cheepers\";",BM.equation)) isa BM.BaseModelicaEquation + @test only(PC.parse_one("der('T') = 'x'",BM.equation)) isa BM.BaseModelicaEquation + @test only(PC.parse_one("otherfun('x') = 'func_arg'", BM.equation)) isa BM.BaseModelicaEquation + @test PC.parse_one(""" + Real 'juice.juice' \"juicy\"; + Real 'fruit' \"fruity\"; + output Real 'output_fruit'; + parameter Real 'doop' = 60; + equation + ('juice'+'fruit')*'blade' = 'puree'; + 'juicy' * 'fruit' = 'juicyfruit'; + """, BM.composition) |> length == 7 + @test PC.parse_one("""JuiceModel + Real 'juice.juice' \"juicy\"; + Real 'fruit' \"fruity\"; + output Real 'output_fruit'; + parameter Real 'doop' = 60; + equation + ('juice'+'fruit')*'blade' = 'puree'; + 'juicy' * 'fruit' = 'juicyfruit'; + end JuiceModel; + """, BM.long_class_specifier) |> length == 10 + + @test only(PC.parse_one(""" + package JuiceModel + model JuiceModel + Real 'juice.juice' \"juicy\"; + Real 'fruit' \"fruity\"; + Real 'T'; + output Real 'output_fruit'; + parameter Real 'soap' = 59; + equation + ('juice'+'fruit' + 100.0)*'blade' = 'puree' "smoothie?"; + 'juicy' * 'fruit' = 'juicyfruit'; + initial equation + 'juicy' = 1000; + 'fruit' = 2000; + end JuiceModel; + end JuiceModel;""",BM.base_modelica)) isa BM.BaseModelicaPackage + newton_path = joinpath( - pathof(BaseModelica), "test", "testfiles", "NewtonCoolingBase.mo") - newton_cooling = BaseModelica.parse_file("testfiles/NewtonCoolingBase.mo") - @test newton_cooling isa BaseModelica.BaseModelicaPackage - newton_system = BaseModelica.baseModelica_to_ModelingToolkit(newton_cooling.model) + pathof(BM), "test", "testfiles", "NewtonCoolingBase.mo") + newton_cooling = BM.parse_file("testfiles/NewtonCoolingBase.mo") + @test newton_cooling isa BM.BaseModelicaPackage + newton_system = BM.baseModelica_to_ModelingToolkit(newton_cooling.model) @test newton_system isa ODESystem @test parse_basemodelica("testfiles/NewtonCoolingBase.mo") isa ODESystem end