This commit is contained in:
NikolajDanger
2022-06-07 21:59:46 +02:00
parent dece7eb03e
commit de697b121e
5 changed files with 119 additions and 21 deletions

View File

@@ -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.

View File

@@ -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]

View File

@@ -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
View File

@@ -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()

View File

@@ -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])