OTPCL interpreter/evaluator.
OTPCL's interpreter effectively revolves around repeatedly calling 2-arity functions ("commands"), the first argument being the actual list of arguments for that function/command, and the second being the current interpretation state (expressed as a tuple of two maps, one with all command definitions and one with all variable definitions). Each command-backing function in turn returns a tuple with a return value and an updated state.
To illustrate: when OTPCL's parser encounters the command invocation foo bar
baz
and sends the corresponding parse tree to the interpreter, the
interpreter in turn calls {Result, NewState} = Fun([bar, baz], State)
(where
Fun
is the value of the foo
key in the first element of the State
tuple,
Result
is the return value for that command, and NewState
is the updated
state).
This means it's pretty straightforward to define an OTPCL command yourself from within Erlang: simply define a 2-arity function where the first argument is a list and the second argument is a 2-element tuple of maps. A module that defines OTPCL commands can/should specify which functions in that module are "OTPCL-aware" in this fashion like so:
-module(my_otpcl_cmds). -export([foo/2, bar/2, baz/2]). -otpcl_cmds([foo, bar, baz]). % OTPCL-aware funs in the module foo([Thing], State) -> {ok, State}. bar([Thing1, Thing2], State) -> {{Thing1, Thing2}, State}. baz([Name, Val], State) -> otpcl_stdlib:set([Name, Val], State).
The interpreter itself is also an OTPCL-aware function in this sense (albeit with a simplification in that it does not require its first argument to be a list; it can take a parse tree directly). It can thus be invoked from within OTPCL:
otpcl> import otpcl_eval {ok,otpcl_eval} otpcl> eval {set foo "howdy~n"} ok otpcl> print $foo howdy ok otpcl> import otpcl_env ok otpcl> eval { ...> set foo "aloha~n" ...> print $foo ...> } [default_state] aloha [ ... pile of interpreter state ... ] otpcl> print $foo howdy ok
In fact, most OTPCL features are in turn implemented as OTPCL-aware command-backing functions; that is: OTPCL exposes its own functionality as OTPCL commands wherever it's possible/practical to do so.
Of course, one may also do this from any OTP application that uses OTPCL, e.g. one written in Erlang:
erl> State0 = otpcl_env:default_state(). [ ... pile of interpreter state ... ] erl> {ok, State1} = otpcl_stdlib:set([foo, <<"howdy~n">>], State0). [ ... pile of interpreter state ... ] erl> {ok, State2} = otpcl_eval:eval("print $foo", State1). howdy [ ... pile of interpreter state ... ]
column_no() = integer()
eval_error() = {error, reason(), state()}
eval_input() = string() | binary() | [token()]
eval_success() = {ok, state()}
filename() = any()
funs() = map()
level() = atom()
line_no() = integer()
position() = {filename(), line_no(), column_no()}
reason() = atom() | {atom(), any()}
token() = {char(), position()}
tree() = {parsed, level(), [tree()] | [token()]}
vars() = map()
eval/1 | Evaluate a string with the default OTPCL starting state. |
eval/2 | Evaluate a string with a custom starting state. |
eval_file/1 | Evaluate the named file with the default OTPCL starting state. |
eval_file/2 | Evaluate the named file with a custom starting state. |
interpret/1 | Interpret the parse nodes with the default OTPCL starting state. |
interpret/2 | Interpret the parse nodes with a custom starting state. |
make_atom/1 | Extract an atom from a token string. |
make_atomic/1 | Extract a float, integer, or atom (in order of preference) from a token string. |
make_binstring/1 | Extract a binary string from a token string. |
make_charstring/1 | Extract a character string from a token string. |
eval(Src::eval_input()) -> eval_success() | eval_error()
Evaluate a string with the default OTPCL starting state.
eval(Src::eval_input(), State::state()) -> eval_success() | eval_error()
Evaluate a string with a custom starting state.
eval_file(Filename::filename()) -> eval_success() | eval_error()
Evaluate the named file with the default OTPCL starting state.
eval_file(Filename::filename(), State::state()) -> eval_success() | eval_error()
Evaluate the named file with a custom starting state.
interpret(Nodes::tree() | [tree()]) -> eval_success() | eval_error()
Interpret the parse nodes with the default OTPCL starting state.
interpret(InvalidNode::tree() | [tree()], State::state()) -> eval_success() | eval_error()
Interpret the parse nodes with a custom starting state.
make_atom(Tokens::[token()]) -> atom()
Extract an atom from a token string. This skips any attempt to check if an atom is a number (which means single-quoted atoms might technically be more efficient than unquoted atoms at the moment...).
make_atomic(Tokens::[token()]) -> atom() | integer() | float()
Extract a float, integer, or atom (in order of preference) from a token string.
make_binstring(Tokens::[token()]) -> binary()
Extract a binary string from a token string.
make_charstring(Tokens::[token()]) -> string()
Extract a character string from a token string.
Generated by EDoc