✨
This commit is contained in:
14
README.md
14
README.md
@@ -1,5 +1,5 @@
|
||||
# About
|
||||
`CENTURION` is the programming language for the modern roman.
|
||||
`CENTVRION` is the programming language for the modern roman.
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -57,7 +57,7 @@ Variable can consist of lower-case letters, numbers, as well as `_`.
|
||||
|
||||
## Data types
|
||||
### NULLUS
|
||||
`NULLUS` is a special kind of data type in `CENTURION`, similar to the `null` value in many other languages. `NULLUS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NULLUS` cannot be evaluated as a boolean.
|
||||
`NULLUS` is a special kind of data type in `CENTVRION`, similar to the `null` value in many other languages. `NULLUS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NULLUS` cannot be evaluated as a boolean.
|
||||
|
||||
### Strings
|
||||
|
||||
@@ -82,7 +82,7 @@ Integers must be written in roman numerals using the following symbols:
|
||||
|
||||
Each of the symbols written by themself is equal to the value of the symbol. Different symbols written from largest to smallest are equal to the sum of the symbols. Two to three of the same symbol written consecutively is equal to the sum of those symbols (only true for `I`s, `X`s, `C`s or `M`s ). A single `I` written before a `V` or `X` is equal to 1 less than the value of the second symbol. Similarly, an `X` written before a `L` or `C` is 10 less than the second symbol, and a `C` written before a `D` or `M` is 100 less than the second symbol.
|
||||
|
||||
Because of the restrictions of roman numerals, numbers above 3.999 are impossible to write in the base `CENTURION` syntax. If numbers of that size are required, see the `MAGNUM` module.
|
||||
Because of the restrictions of roman numerals, numbers above 3.999 are impossible to write in the base `CENTVRION` syntax. If numbers of that size are required, see the `MAGNUM` module.
|
||||
|
||||
The number 0 can be expressed with the keyword `NULLUS`.
|
||||
|
||||
@@ -90,7 +90,7 @@ The number 0 can be expressed with the keyword `NULLUS`.
|
||||
Negative numbers can be expressed as `NULLUS` minus the value. For an explicit definition of negative numbers, see the `SUBNULLA` module.
|
||||
|
||||
### Floats
|
||||
The base `CENTURION` syntax does not allow for floats. However, the `FRACTIO` module adds a syntax for fractions.
|
||||
The base `CENTVRION` syntax does not allow for floats. However, the `FRACTIO` module adds a syntax for fractions.
|
||||
|
||||
### Booleans
|
||||
Booleans are denoted with the keywords `VERITAS` for true and `FALSITAS` for false.
|
||||
@@ -227,18 +227,18 @@ DICE (INVOCA square XI)
|
||||
### LONGITUDO
|
||||
|
||||
## Modules
|
||||
Modules are additions to the base `CENTURION` syntax. They add or change certain features. Modules are included in your code by having
|
||||
Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having
|
||||
|
||||
```VOCA %MODULE NAME%```
|
||||
|
||||
In the beginning of your source file.
|
||||
|
||||
Unlike many other programming languages with modules, the modules in `CENTURION` are not libraries that can be "imported" from other scripts written in the language. They are features of the compiler, disabled by default.
|
||||
Unlike many other programming languages with modules, the modules in `CENTVRION` are not libraries that can be "imported" from other scripts written in the language. They are features of the compiler, disabled by default.
|
||||
|
||||
### FORS
|
||||
```VOCA FORS```
|
||||
|
||||
The `FORS` module allows you to add randomness to your `CENTURION` program. It adds 2 new built-in functions: `FORTIS_NUMERUS int int` and `FORTIS_ELECTIONIS ['a]`.
|
||||
The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 2 new built-in functions: `FORTIS_NUMERUS int int` and `FORTIS_ELECTIONIS ['a]`.
|
||||
|
||||
`FORTIS_NUMERUS int int` picks a random int in the (inclusive) range of the two given ints.
|
||||
|
||||
|
||||
103
ast_nodes.py
103
ast_nodes.py
@@ -1,5 +1,18 @@
|
||||
import random
|
||||
import roman
|
||||
|
||||
from rply.token import BaseBox
|
||||
|
||||
NUMERALS = [
|
||||
("I", 1),
|
||||
("V", 5),
|
||||
("X", 10),
|
||||
("L", 50),
|
||||
("C", 100),
|
||||
("D", 500),
|
||||
("M", 1000)
|
||||
]
|
||||
|
||||
def rep_join(l):
|
||||
format_string = ',\n'.join(
|
||||
[repr(i) if not isinstance(i, str) else i for i in l]
|
||||
@@ -10,6 +23,9 @@ def rep_join(l):
|
||||
|
||||
return format_string
|
||||
|
||||
def num_to_int(n):
|
||||
return roman.fromRoman(n)
|
||||
|
||||
class ExpressionStatement(BaseBox):
|
||||
def __init__(self, expression) -> None:
|
||||
self.expression = expression
|
||||
@@ -18,7 +34,7 @@ class ExpressionStatement(BaseBox):
|
||||
return self.expression.__repr__()
|
||||
|
||||
def eval(self, vtable, ftable, modules):
|
||||
self.expression.eval(vtable, ftable, modules)
|
||||
self.expression.eval(vtable.copy(), ftable.copy(), modules)
|
||||
return vtable, ftable
|
||||
|
||||
class String(BaseBox):
|
||||
@@ -28,6 +44,9 @@ class String(BaseBox):
|
||||
def __repr__(self):
|
||||
return f"String({self.value})"
|
||||
|
||||
def eval(self, *_):
|
||||
return self.value
|
||||
|
||||
class Numeral(BaseBox):
|
||||
def __init__(self, value) -> None:
|
||||
self.value = value
|
||||
@@ -35,6 +54,9 @@ class Numeral(BaseBox):
|
||||
def __repr__(self):
|
||||
return f"Numeral({self.value})"
|
||||
|
||||
def eval(self, *_):
|
||||
return num_to_int(self.value)
|
||||
|
||||
class Bool(BaseBox):
|
||||
def __init__(self, value) -> None:
|
||||
self.value = value
|
||||
@@ -42,6 +64,9 @@ class Bool(BaseBox):
|
||||
def __repr__(self):
|
||||
return f"Bool({self.value})"
|
||||
|
||||
def eval(self, *_):
|
||||
return self.value
|
||||
|
||||
class ModuleCall(BaseBox):
|
||||
def __init__(self, module_name) -> None:
|
||||
self.module_name = module_name
|
||||
@@ -56,6 +81,9 @@ class ID(BaseBox):
|
||||
def __repr__(self) -> str:
|
||||
return f"ID({self.name})"
|
||||
|
||||
def eval(self, vtable, *_):
|
||||
return vtable[self.name]
|
||||
|
||||
class Designa(BaseBox):
|
||||
def __init__(self, variable: ID, value) -> None:
|
||||
self.id = variable
|
||||
@@ -67,7 +95,9 @@ class Designa(BaseBox):
|
||||
return f"Designa(\n {id_string},\n {value_string}\n)"
|
||||
|
||||
def eval(self, vtable, ftable, modules):
|
||||
vtable[self.id.name] = self.value.eval(vtable, ftable, modules)
|
||||
vtable[self.id.name] = self.value.eval(
|
||||
vtable.copy(), ftable.copy(), modules
|
||||
)
|
||||
return vtable, ftable
|
||||
|
||||
class Defini(BaseBox):
|
||||
@@ -92,6 +122,14 @@ class Redi(BaseBox):
|
||||
values_string = f"[{rep_join(self.values)}]"
|
||||
return f"Redi({values_string})"
|
||||
|
||||
class Erumpe(BaseBox):
|
||||
def __repr__(self) -> str:
|
||||
return "Erumpe()"
|
||||
|
||||
def eval(self, vtable, ftable, _):
|
||||
vtable["ERUMPE"] = True
|
||||
return vtable, ftable
|
||||
|
||||
class Nullus(BaseBox):
|
||||
def __repr__(self) -> str:
|
||||
return "Nullus()"
|
||||
@@ -109,6 +147,19 @@ class BinOp(BaseBox):
|
||||
binop_string = rep_join([self.left, self.right, self.op])
|
||||
return f"BinOp({binop_string})"
|
||||
|
||||
def eval(self, vtable, ftable, modules):
|
||||
left = self.left.eval(vtable.copy(), ftable.copy(), modules)
|
||||
right = self.right.eval(vtable.copy(), ftable.copy(), modules)
|
||||
match self.op:
|
||||
case "SYMBOL_PLUS":
|
||||
return left + right
|
||||
case "KEYWORD_MINUS":
|
||||
return left < right
|
||||
case "KEYWORD_PLUS":
|
||||
return left > right
|
||||
case _:
|
||||
raise Exception(self.op)
|
||||
|
||||
class SiStatement(BaseBox):
|
||||
def __init__(self, test, statements, else_part) -> None:
|
||||
self.test = test
|
||||
@@ -122,6 +173,20 @@ class SiStatement(BaseBox):
|
||||
si_string = rep_join([test, statements, else_part])
|
||||
return f"Si({si_string})"
|
||||
|
||||
def eval(self, vtable, ftable, modules):
|
||||
if self.test.eval(vtable, ftable, modules):
|
||||
for statement in self.statements:
|
||||
vtable, ftable = statement.eval(
|
||||
vtable, ftable, modules
|
||||
)
|
||||
else:
|
||||
for statement in self.else_part:
|
||||
vtable, ftable = statement.eval(
|
||||
vtable, ftable, modules
|
||||
)
|
||||
|
||||
return vtable, ftable
|
||||
|
||||
class DumStatement(BaseBox):
|
||||
def __init__(self, test, statements) -> None:
|
||||
self.test = test
|
||||
@@ -135,7 +200,16 @@ class DumStatement(BaseBox):
|
||||
|
||||
def eval(self, vtable, ftable, modules):
|
||||
while not self.test.eval(vtable, ftable, modules):
|
||||
pass
|
||||
for statement in self.statements:
|
||||
vtable, ftable = statement.eval(
|
||||
vtable, ftable, modules
|
||||
)
|
||||
if vtable["ERUMPE"]:
|
||||
break
|
||||
|
||||
if vtable["ERUMPE"]:
|
||||
vtable["ERUMPE"] = False
|
||||
break
|
||||
|
||||
return vtable, ftable
|
||||
|
||||
@@ -159,8 +233,25 @@ class BuiltIn(BaseBox):
|
||||
builtin_string = rep_join([self.builtin, parameter_string])
|
||||
return f"Builtin({builtin_string})"
|
||||
|
||||
def eval(self, vtable, ftable, _):
|
||||
return None
|
||||
def eval(self, vtable, ftable, modules):
|
||||
parameters = [
|
||||
i.eval(vtable.copy(), ftable.copy(), modules)
|
||||
for i in self.parameters
|
||||
]
|
||||
|
||||
match self.builtin:
|
||||
case "AUDI_NUMERUS":
|
||||
return num_to_int(input())
|
||||
case "DICE":
|
||||
print(' '.join(parameters))
|
||||
return None
|
||||
case "ERUMPE":
|
||||
vtable["ERUMPE"] = True
|
||||
return None
|
||||
case "FORTIS_NUMERUS":
|
||||
return random.randint(parameters[0], parameters[1])
|
||||
case _:
|
||||
raise Exception(self.builtin)
|
||||
|
||||
class Program(BaseBox):
|
||||
def __init__(self, module_calls: list[ModuleCall], statements) -> None:
|
||||
@@ -173,7 +264,7 @@ class Program(BaseBox):
|
||||
return f"{modules_string},\n{statements_string}"
|
||||
|
||||
def eval(self):
|
||||
vtable = {}
|
||||
vtable = {"ERUMPE": False}
|
||||
ftable = {}
|
||||
modules = [module.module_name for module in self.modules]
|
||||
|
||||
|
||||
6
lexer.py
6
lexer.py
@@ -1,11 +1,14 @@
|
||||
from rply import LexerGenerator
|
||||
|
||||
valid_characters = '|'.join(list("abcdefghiklmnopqrstvxyz_"))
|
||||
|
||||
keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
||||
"ALUID",
|
||||
"DEFINI",
|
||||
"DESIGNA",
|
||||
"DONICUM",
|
||||
"DUM",
|
||||
"ERUMPE",
|
||||
"EST",
|
||||
"FACE",
|
||||
"FALSITAS",
|
||||
@@ -27,7 +30,6 @@ builtin_tokens = [("BUILTIN", i) for i in [
|
||||
"AUDI_NUMERUS",
|
||||
"AUDI",
|
||||
"DICE",
|
||||
"ERUMPE",
|
||||
"FORTIS_NUMERUS",
|
||||
"FORTIS_ELECTIONIS",
|
||||
"LONGITUDO"
|
||||
@@ -69,7 +71,7 @@ all_tokens = (
|
||||
symbol_tokens +
|
||||
data_tokens +
|
||||
whitespace_tokens +
|
||||
[("ID", r"([a-z]|_)+")]
|
||||
[("ID", f"({valid_characters})+")]
|
||||
)
|
||||
|
||||
class Lexer():
|
||||
|
||||
11
main.py
11
main.py
@@ -5,13 +5,13 @@ text_input = """
|
||||
VOCA FORS
|
||||
|
||||
DESIGNA correct UT FORTIS_NUMERUS I C
|
||||
DESIGNA guess UT NULLUS
|
||||
DESIGNA gvess UT NULLUS
|
||||
|
||||
DUM FALSITAS FACE {
|
||||
DESIGNA guess UT AUDI_NUMERUS
|
||||
SI guess MINUS correct TUNC {
|
||||
DESIGNA gvess UT AUDI_NUMERUS
|
||||
SI gvess MINUS correct TUNC {
|
||||
DICE "Too low!"
|
||||
} ALUID SI guess PLUS correct TUNC {
|
||||
} ALUID SI gvess PLUS correct TUNC {
|
||||
DICE "Too high!"
|
||||
} ALUID {
|
||||
ERUMPE
|
||||
@@ -28,4 +28,5 @@ parser = pg.get_parser()
|
||||
|
||||
tokens = lexer.lex(text_input)
|
||||
|
||||
print(parser.parse(tokens))
|
||||
x = parser.parse(tokens)
|
||||
x.eval()
|
||||
|
||||
@@ -77,7 +77,7 @@ class Parser():
|
||||
|
||||
@self.pg.production('expression : DATA_STRING')
|
||||
def expression_string(tokens):
|
||||
return ast_nodes.String(tokens[0].value)
|
||||
return ast_nodes.String(tokens[0].value[1:-1])
|
||||
|
||||
@self.pg.production('expression : DATA_NUMERAL')
|
||||
def expression_numeral(tokens):
|
||||
@@ -120,6 +120,10 @@ class Parser():
|
||||
def dum_statement(tokens):
|
||||
return tokens[0]
|
||||
|
||||
@self.pg.production('statement : KEYWORD_ERUMPE')
|
||||
def erumpe(_):
|
||||
return ast_nodes.Erumpe()
|
||||
|
||||
@self.pg.production('si_statement : KEYWORD_SI expression KEYWORD_TUNC SYMBOL_LCURL opt_newline statements opt_newline SYMBOL_RCURL opt_newline aluid_statement')
|
||||
def si(tokens):
|
||||
return ast_nodes.SiStatement(tokens[1], tokens[5], tokens[9])
|
||||
|
||||
Reference in New Issue
Block a user